#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
#define CK_NAME "org.freedesktop.ConsoleKit"
-#define CK_PATH "/org/freedesktop/ConsoleKit"
-#define CK_INTERFACE "org.freedesktop.ConsoleKit"
#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager"
#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
#define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat"
#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
+#define GDM_DBUS_TYPE_G_OBJECT_PATH_ARRAY (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
+
/* Prefs Defaults */
-#define DEFAULT_ALLOW_ROOT TRUE
-#define DEFAULT_MAX_ICON_SIZE 128
-#define DEFAULT_USER_MAX_FILE 65536
#ifdef __sun
-#define DEFAULT_MINIMAL_UID 100
+#define FALLBACK_MINIMAL_UID 100
#else
-#define DEFAULT_MINIMAL_UID 500
+#define FALLBACK_MINIMAL_UID 500
#endif
#ifndef _PATH_SHELLS
#endif
#define PATH_PASSWD "/etc/passwd"
-#define DEFAULT_GLOBAL_FACE_DIR DATADIR "/faces"
-#define DEFAULT_USER_ICON "stock_person"
-#define DEFAULT_EXCLUDE { "bin", \
- "root", \
- "daemon", \
- "adm", \
- "lp", \
- "sync", \
- "shutdown", \
- "halt", \
- "mail", \
- "news", \
- "uucp", \
- "operator", \
- "nobody", \
- "gdm", \
- "postgres", \
- "pvm", \
- "rpm", \
- "nfsnobody", \
- "pcap", \
- NULL }
+#ifndef GDM_USERNAME
+#define GDM_USERNAME "gdm"
+#endif
+
+#define RELOAD_PASSWD_THROTTLE_SECS 5
+
+/* approximately two months */
+#define LOGIN_FREQUENCY_TIME_WINDOW_SECS (60 * 24 * 60 * 60)
+
+#define ACCOUNTS_NAME "org.freedesktop.Accounts"
+#define ACCOUNTS_PATH "/org/freedesktop/Accounts"
+#define ACCOUNTS_INTERFACE "org.freedesktop.Accounts"
+
+typedef enum {
+ GDM_USER_MANAGER_SEAT_STATE_UNLOADED = 0,
+ GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID,
+ GDM_USER_MANAGER_SEAT_STATE_GET_ID,
+ GDM_USER_MANAGER_SEAT_STATE_GET_PROXY,
+ GDM_USER_MANAGER_SEAT_STATE_LOADED,
+} GdmUserManagerSeatState;
+
+typedef struct
+{
+ GdmUserManagerSeatState state;
+ char *id;
+ char *session_id;
+ union {
+ DBusGProxyCall *get_current_session_call;
+ DBusGProxyCall *get_seat_id_call;
+ };
+
+ DBusGProxy *proxy;
+} GdmUserManagerSeat;
+
+typedef enum {
+ GDM_USER_MANAGER_NEW_SESSION_STATE_UNLOADED = 0,
+ GDM_USER_MANAGER_NEW_SESSION_STATE_GET_PROXY,
+ GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID,
+ GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY,
+ GDM_USER_MANAGER_NEW_SESSION_STATE_MAYBE_ADD,
+ GDM_USER_MANAGER_NEW_SESSION_STATE_LOADED,
+} GdmUserManagerNewSessionState;
+
+typedef struct
+{
+ GdmUserManager *manager;
+ GdmUserManagerNewSessionState state;
+ char *id;
+
+ union {
+ DBusGProxyCall *get_unix_user_call;
+ DBusGProxyCall *get_x11_display_call;
+ };
+
+ DBusGProxy *proxy;
+
+ uid_t uid;
+ char *x11_display;
+} GdmUserManagerNewSession;
+
+typedef enum {
+ GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED = 0,
+ GDM_USER_MANAGER_GET_USER_STATE_WAIT_FOR_LOADED,
+ GDM_USER_MANAGER_GET_USER_STATE_ASK_ACCOUNTS_SERVICE,
+ GDM_USER_MANAGER_GET_USER_STATE_FETCHED
+} GdmUserManagerGetUserState;
+
+typedef struct
+{
+ GdmUserManager *manager;
+ GdmUserManagerGetUserState state;
+ GdmUser *user;
+ char *username;
+ char *object_path;
+
+ DBusGProxyCall *call;
+} GdmUserManagerFetchUserRequest;
struct GdmUserManagerPrivate
{
- GHashTable *users;
+ GHashTable *users_by_name;
+ GHashTable *users_by_object_path;
GHashTable *sessions;
- GHashTable *exclusions;
GHashTable *shells;
DBusGConnection *connection;
- DBusGProxy *seat_proxy;
- char *seat_id;
+ DBusGProxyCall *get_sessions_call;
+ DBusGProxy *accounts_proxy;
+
+ GdmUserManagerSeat seat;
+
+ GSList *new_sessions;
+ GSList *new_users;
+ GSList *fetch_user_requests;
GFileMonitor *passwd_monitor;
GFileMonitor *shells_monitor;
- guint reload_id;
+ GSList *exclude_usernames;
+ GSList *include_usernames;
+ gboolean include_all;
+
+ gboolean load_passwd_pending;
+
+ guint load_id;
+ guint reload_passwd_id;
guint ck_history_id;
+ guint ck_history_watchdog_id;
+ GPid ck_history_pid;
- guint8 users_dirty : 1;
+ gboolean is_loaded;
+ gboolean has_multiple_users;
+ gboolean listing_cached_users;
+};
+
+enum {
+ PROP_0,
+ PROP_INCLUDE_ALL,
+ PROP_INCLUDE_USERNAMES_LIST,
+ PROP_EXCLUDE_USERNAMES_LIST,
+ PROP_IS_LOADED,
+ PROP_HAS_MULTIPLE_USERS
};
enum {
- LOADING_USERS,
- USERS_LOADED,
USER_ADDED,
USER_REMOVED,
USER_IS_LOGGED_IN_CHANGED,
- USER_LOGIN_FREQUENCY_CHANGED,
+ USER_CHANGED,
LAST_SIGNAL
};
static void gdm_user_manager_init (GdmUserManager *user_manager);
static void gdm_user_manager_finalize (GObject *object);
+static void load_users_manually (GdmUserManager *manager);
+static void monitor_local_users (GdmUserManager *manager);
+static void load_seat_incrementally (GdmUserManager *manager);
+static void unload_seat (GdmUserManager *manager);
+static void load_users (GdmUserManager *manager);
+static void queue_load_seat_and_users (GdmUserManager *manager);
+static void monitor_local_users (GdmUserManager *manager);
+
+static void load_new_session_incrementally (GdmUserManagerNewSession *new_session);
+static void set_is_loaded (GdmUserManager *manager, gboolean is_loaded);
+
+static void on_new_user_loaded (GdmUser *user,
+ GParamSpec *pspec,
+ GdmUserManager *manager);
+static void give_up_and_fetch_user_locally (GdmUserManager *manager,
+ GdmUserManagerFetchUserRequest *request);
+static void fetch_user_locally (GdmUserManager *manager,
+ GdmUser *user,
+ const char *username);
+static void fetch_user_incrementally (GdmUserManagerFetchUserRequest *request);
+
+static void maybe_set_is_loaded (GdmUserManager *manager);
static gpointer user_manager_object = NULL;
G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
res = g_spawn_command_line_async ("gdmflexiserver -s", &error);
if (! res) {
- g_warning ("Unable to start new login: %s", error->message);
- g_error_free (error);
- }
-
- return res;
-}
-
-/* needs to stay in sync with gdm-slave */
-static char *
-_get_primary_user_session_id (GdmUserManager *manager,
- GdmUser *user)
-{
- gboolean res;
- gboolean can_activate_sessions;
- GError *error;
- GList *sessions;
- GList *l;
- char *primary_ssid;
-
- if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
- g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
- return NULL;
- }
-
- primary_ssid = NULL;
- sessions = NULL;
-
- g_debug ("GdmUserManager: checking if seat can activate sessions");
-
- error = NULL;
- res = dbus_g_proxy_call (manager->priv->seat_proxy,
- "CanActivateSessions",
- &error,
- G_TYPE_INVALID,
- G_TYPE_BOOLEAN, &can_activate_sessions,
- G_TYPE_INVALID);
- if (! res) {
- g_warning ("unable to determine if seat can activate sessions: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
-
- if (! can_activate_sessions) {
- g_debug ("GdmUserManager: seat is unable to activate sessions");
- goto out;
- }
-
- sessions = gdm_user_get_sessions (user);
- if (sessions == NULL) {
- g_warning ("unable to determine sessions for user: %s",
- gdm_user_get_user_name (user));
- goto out;
- }
-
- for (l = sessions; l != NULL; l = l->next) {
- const char *ssid;
-
- ssid = l->data;
-
- /* FIXME: better way to choose? */
- if (ssid != NULL) {
- primary_ssid = g_strdup (ssid);
- break;
+ if (error != NULL) {
+ g_warning ("Unable to start new login: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Unable to start new login");
}
}
- out:
-
- return primary_ssid;
+ return res;
}
static gboolean
G_TYPE_STRING, &session_type,
G_TYPE_INVALID);
if (! res) {
- g_debug ("Failed to identify the session type: %s", error->message);
- g_error_free (error);
+ if (error != NULL) {
+ g_debug ("GdmUserManager: Failed to identify the session type: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("GdmUserManager: Failed to identify the session type");
+ }
goto out;
}
char *primary_ssid;
int i;
- if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
- g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
+ if (manager->priv->seat.id == NULL || manager->priv->seat.id[0] == '\0') {
+ g_debug ("GdmUserManager: display seat ID is not set; can't switch sessions");
return NULL;
}
primary_ssid = NULL;
sessions = NULL;
- g_debug ("GdmSlave: checking if seat can activate sessions");
-
- error = NULL;
- res = dbus_g_proxy_call (manager->priv->seat_proxy,
- "CanActivateSessions",
- &error,
- G_TYPE_INVALID,
- G_TYPE_BOOLEAN, &can_activate_sessions,
- G_TYPE_INVALID);
- if (! res) {
- g_warning ("unable to determine if seat can activate sessions: %s",
- error->message);
- g_error_free (error);
- goto out;
- }
+ can_activate_sessions = gdm_user_manager_can_switch (manager);
if (! can_activate_sessions) {
- g_debug ("GdmSlave: seat is unable to activate sessions");
+ g_debug ("GdmUserManager: seat is unable to activate sessions");
goto out;
}
error = NULL;
- res = dbus_g_proxy_call (manager->priv->seat_proxy,
+ res = dbus_g_proxy_call (manager->priv->seat.proxy,
"GetSessions",
&error,
G_TYPE_INVALID,
dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions,
G_TYPE_INVALID);
if (! res) {
- g_warning ("unable to determine sessions for user: %s",
- error->message);
- g_error_free (error);
+ if (error != NULL) {
+ g_warning ("unable to determine sessions for user: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("unable to determine sessions for user");
+ }
goto out;
}
char *ssid;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
+ g_return_val_if_fail (manager->priv->is_loaded, FALSE);
ret = FALSE;
ssid = _get_login_window_session_id (manager);
if (ssid != NULL) {
- res = activate_session_id (manager, manager->priv->seat_id, ssid);
+ res = activate_session_id (manager, manager->priv->seat.id, ssid);
if (res) {
ret = TRUE;
}
return ret;
}
+gboolean
+gdm_user_manager_can_switch (GdmUserManager *manager)
+{
+ gboolean res;
+ gboolean can_activate_sessions;
+ GError *error;
+
+ if (!manager->priv->is_loaded) {
+ g_debug ("GdmUserManager: Unable to switch sessions until fully loaded");
+ return FALSE;
+ }
+
+ if (manager->priv->seat.id == NULL || manager->priv->seat.id[0] == '\0') {
+ g_debug ("GdmUserManager: display seat ID is not set; can't switch sessions");
+ return FALSE;
+ }
+
+ g_debug ("GdmUserManager: checking if seat can activate sessions");
+
+ error = NULL;
+ res = dbus_g_proxy_call (manager->priv->seat.proxy,
+ "CanActivateSessions",
+ &error,
+ G_TYPE_INVALID,
+ G_TYPE_BOOLEAN, &can_activate_sessions,
+ G_TYPE_INVALID);
+ if (! res) {
+ if (error != NULL) {
+ g_warning ("unable to determine if seat can activate sessions: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("unable to determine if seat can activate sessions");
+ }
+ return FALSE;
+ }
+
+ return can_activate_sessions;
+}
+
gboolean
gdm_user_manager_activate_user_session (GdmUserManager *manager,
GdmUser *user)
{
gboolean ret;
- char *ssid;
+ const char *ssid;
gboolean res;
+ gboolean can_activate_sessions;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
g_return_val_if_fail (GDM_IS_USER (user), FALSE);
+ g_return_val_if_fail (manager->priv->is_loaded, FALSE);
ret = FALSE;
- ssid = _get_primary_user_session_id (manager, user);
+ can_activate_sessions = gdm_user_manager_can_switch (manager);
+
+ if (! can_activate_sessions) {
+ g_debug ("GdmUserManager: seat is unable to activate sessions");
+ goto out;
+ }
+
+ ssid = gdm_user_get_primary_session_id (user);
if (ssid == NULL) {
goto out;
}
- res = activate_session_id (manager, manager->priv->seat_id, ssid);
+ res = activate_session_id (manager, manager->priv->seat.id, ssid);
if (! res) {
g_debug ("GdmUserManager: unable to activate session: %s", ssid);
goto out;
{
guint nsessions;
+ if (! manager->priv->is_loaded) {
+ return;
+ }
+
nsessions = gdm_user_get_num_sessions (user);
g_debug ("GdmUserManager: sessions changed user=%s num=%d",
}
static void
-on_user_icon_changed (GdmUser *user,
- GdmUserManager *manager)
+on_user_changed (GdmUser *user,
+ GdmUserManager *manager)
{
- g_debug ("GdmUserManager: user icon changed");
+ if (manager->priv->is_loaded) {
+ g_debug ("GdmUserManager: user changed");
+ g_signal_emit (manager, signals[USER_CHANGED], 0, user);
+ }
}
-static char *
-get_seat_id_for_session (DBusGConnection *connection,
- const char *session_id)
+static void
+on_get_seat_id_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ GdmUserManager *manager)
{
- DBusGProxy *proxy;
- GError *error;
- char *seat_id;
- gboolean res;
-
- proxy = NULL;
- seat_id = NULL;
+ GError *error;
+ char *seat_id;
+ gboolean res;
- proxy = dbus_g_proxy_new_for_name (connection,
- CK_NAME,
- session_id,
- CK_SESSION_INTERFACE);
- if (proxy == NULL) {
- g_warning ("Failed to connect to the ConsoleKit session object");
- goto out;
- }
+ g_assert (manager->priv->seat.get_seat_id_call == call);
error = NULL;
- res = dbus_g_proxy_call (proxy,
- "GetSeatId",
- &error,
- G_TYPE_INVALID,
- DBUS_TYPE_G_OBJECT_PATH, &seat_id,
- G_TYPE_INVALID);
+ seat_id = NULL;
+ res = dbus_g_proxy_end_call (proxy,
+ call,
+ &error,
+ DBUS_TYPE_G_OBJECT_PATH,
+ &seat_id,
+ G_TYPE_INVALID);
+ manager->priv->seat.get_seat_id_call = NULL;
+ g_object_unref (proxy);
+
if (! res) {
- g_debug ("Failed to identify the current seat: %s", error->message);
- g_error_free (error);
- }
- out:
- if (proxy != NULL) {
- g_object_unref (proxy);
+ if (error != NULL) {
+ g_debug ("Failed to identify the seat of the "
+ "current session: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("Failed to identify the seat of the "
+ "current session");
+ }
+ unload_seat (manager);
+ maybe_set_is_loaded (manager);
+ return;
}
- return seat_id;
+ g_debug ("GdmUserManager: Found current seat: %s", seat_id);
+
+ manager->priv->seat.id = seat_id;
+ manager->priv->seat.state++;
+
+ load_seat_incrementally (manager);
}
-static char *
-get_x11_display_for_session (DBusGConnection *connection,
- const char *session_id)
+static void
+get_seat_id_for_current_session (GdmUserManager *manager)
{
DBusGProxy *proxy;
- GError *error;
- char *x11_display;
- gboolean res;
-
- proxy = NULL;
- x11_display = NULL;
+ DBusGProxyCall *call;
- proxy = dbus_g_proxy_new_for_name (connection,
+ proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
CK_NAME,
- session_id,
+ manager->priv->seat.session_id,
CK_SESSION_INTERFACE);
if (proxy == NULL) {
g_warning ("Failed to connect to the ConsoleKit session object");
- goto out;
+ goto failed;
}
- error = NULL;
- res = dbus_g_proxy_call (proxy,
- "GetX11Display",
- &error,
- G_TYPE_INVALID,
- G_TYPE_STRING, &x11_display,
- G_TYPE_INVALID);
- if (! res) {
- g_debug ("Failed to identify the x11 display: %s", error->message);
- g_error_free (error);
+ call = dbus_g_proxy_begin_call (proxy,
+ "GetSeatId",
+ (DBusGProxyCallNotify)
+ on_get_seat_id_finished,
+ manager,
+ NULL,
+ G_TYPE_INVALID);
+ if (call == NULL) {
+ g_warning ("GdmUserManager: failed to make GetSeatId call");
+ goto failed;
}
- out:
+
+ manager->priv->seat.get_seat_id_call = call;
+
+ return;
+
+failed:
if (proxy != NULL) {
g_object_unref (proxy);
}
- return x11_display;
+ unload_seat (manager);
}
-static gboolean
-maybe_add_session_for_user (GdmUserManager *manager,
- GdmUser *user,
- const char *ssid)
+static gint
+match_name_cmpfunc (gconstpointer a,
+ gconstpointer b)
{
- char *sid;
- char *x11_display;
- gboolean ret;
+ return g_strcmp0 ((char *) a,
+ (char *) b);
+}
- ret = FALSE;
- sid = NULL;
- x11_display = NULL;
+static gboolean
+username_in_exclude_list (GdmUserManager *manager,
+ const char *username)
+{
+ GSList *found;
+ gboolean ret = FALSE;
- /* skip if on another seat */
- sid = get_seat_id_for_session (manager->priv->connection, ssid);
- if (sid == NULL
- || manager->priv->seat_id == NULL
- || strcmp (sid, manager->priv->seat_id) != 0) {
- g_debug ("GdmUserManager: not adding session on other seat: %s", ssid);
- goto out;
+ /* always exclude the "gdm" user. */
+ if (username == NULL || (strcmp (username, GDM_USERNAME) == 0)) {
+ return TRUE;
}
- /* skip if doesn't have an x11 display */
- x11_display = get_x11_display_for_session (manager->priv->connection, ssid);
- if (x11_display == NULL || x11_display[0] == '\0') {
- g_debug ("GdmUserManager: not adding session without a x11 display: %s", ssid);
- goto out;
+ if (manager->priv->exclude_usernames != NULL) {
+ found = g_slist_find_custom (manager->priv->exclude_usernames,
+ username,
+ match_name_cmpfunc);
+ if (found != NULL) {
+ ret = TRUE;
+ }
}
- if (g_hash_table_lookup (manager->priv->exclusions, gdm_user_get_user_name (user))) {
- g_debug ("GdmUserManager: excluding user '%s'", gdm_user_get_user_name (user));
- goto out;
- }
+ return ret;
+}
+static void
+add_session_for_user (GdmUserManager *manager,
+ GdmUser *user,
+ const char *ssid)
+{
g_hash_table_insert (manager->priv->sessions,
g_strdup (ssid),
g_strdup (gdm_user_get_user_name (user)));
_gdm_user_add_session (user, ssid);
g_debug ("GdmUserManager: added session for user: %s", gdm_user_get_user_name (user));
-
- ret = TRUE;
-
- out:
- g_free (sid);
- g_free (x11_display);
-
- return ret;
}
static void
-add_sessions_for_user (GdmUserManager *manager,
- GdmUser *user)
+set_has_multiple_users (GdmUserManager *manager,
+ gboolean has_multiple_users)
{
- DBusGProxy *proxy;
- GError *error;
- gboolean res;
- guint32 uid;
- GPtrArray *sessions;
- int i;
-
- proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
- CK_NAME,
- CK_MANAGER_PATH,
- CK_MANAGER_INTERFACE);
- if (proxy == NULL) {
- g_warning ("Failed to connect to the ConsoleKit manager object");
- goto out;
+ if (manager->priv->has_multiple_users != has_multiple_users) {
+ manager->priv->has_multiple_users = has_multiple_users;
+ g_object_notify (G_OBJECT (manager), "has-multiple-users");
}
+}
- uid = gdm_user_get_uid (user);
+static GdmUser *
+create_new_user (GdmUserManager *manager)
+{
+ GdmUser *user;
- g_debug ("Getting list of sessions for user %u", uid);
+ user = g_object_new (GDM_TYPE_USER, NULL);
- error = NULL;
- res = dbus_g_proxy_call (proxy,
- "GetSessionsForUnixUser",
- &error,
- G_TYPE_UINT, uid,
- G_TYPE_INVALID,
- dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
- &sessions,
- G_TYPE_INVALID);
- if (! res) {
- g_debug ("Failed to find sessions for user: %s", error->message);
- g_error_free (error);
- goto out;
- }
+ manager->priv->new_users = g_slist_prepend (manager->priv->new_users, user);
- g_debug ("Found %d sessions for user %s", sessions->len, gdm_user_get_user_name (user));
+ g_signal_connect (user, "notify::is-loaded", G_CALLBACK (on_new_user_loaded), manager);
- for (i = 0; i < sessions->len; i++) {
- char *ssid;
+ return g_object_ref (user);
+}
- ssid = g_ptr_array_index (sessions, i);
- maybe_add_session_for_user (manager, user, ssid);
- }
+static void
+add_user (GdmUserManager *manager,
+ GdmUser *user)
+{
+ const char *object_path;
- g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
- g_ptr_array_free (sessions, TRUE);
+ g_hash_table_insert (manager->priv->users_by_name,
+ g_strdup (gdm_user_get_user_name (user)),
+ g_object_ref (user));
- out:
- if (proxy != NULL) {
- g_object_unref (proxy);
+ object_path = gdm_user_get_object_path (user);
+ if (object_path != NULL) {
+ g_hash_table_insert (manager->priv->users_by_object_path,
+ (gpointer) object_path,
+ g_object_ref (user));
}
-}
-
-static GdmUser *
-create_user (GdmUserManager *manager)
-{
- GdmUser *user;
- user = g_object_new (GDM_TYPE_USER, "manager", manager, NULL);
g_signal_connect (user,
"sessions-changed",
G_CALLBACK (on_user_sessions_changed),
manager);
g_signal_connect (user,
- "icon-changed",
- G_CALLBACK (on_user_icon_changed),
+ "changed",
+ G_CALLBACK (on_user_changed),
manager);
- return user;
+
+ if (manager->priv->is_loaded) {
+ g_signal_emit (manager, signals[USER_ADDED], 0, user);
+ }
+
+ if (g_hash_table_size (manager->priv->users_by_name) > 1) {
+ set_has_multiple_users (manager, TRUE);
+ }
}
static void
-add_user (GdmUserManager *manager,
- GdmUser *user)
+remove_user (GdmUserManager *manager,
+ GdmUser *user)
{
- add_sessions_for_user (manager, user);
- g_hash_table_insert (manager->priv->users,
- g_strdup (gdm_user_get_user_name (user)),
- g_object_ref (user));
+ g_object_ref (user);
+
+ g_signal_handlers_disconnect_by_func (user, on_user_changed, manager);
+ g_signal_handlers_disconnect_by_func (user, on_user_sessions_changed, manager);
+ if (gdm_user_get_object_path (user) != NULL) {
+ g_hash_table_remove (manager->priv->users_by_object_path, gdm_user_get_object_path (user));
+ }
+ g_hash_table_remove (manager->priv->users_by_name, gdm_user_get_user_name (user));
+
+ if (manager->priv->is_loaded) {
+ g_signal_emit (manager, signals[USER_REMOVED], 0, user);
+ }
- g_signal_emit (manager, signals[USER_ADDED], 0, user);
+ g_object_unref (user);
+
+ if (g_hash_table_size (manager->priv->users_by_name) > 1) {
+ set_has_multiple_users (manager, FALSE);
+ }
}
-static GdmUser *
-add_new_user_for_pwent (GdmUserManager *manager,
- struct passwd *pwent)
+static void
+on_new_user_loaded (GdmUser *user,
+ GParamSpec *pspec,
+ GdmUserManager *manager)
{
- GdmUser *user;
+ const char *username;
+ GdmUser *old_user;
+
+ if (!gdm_user_is_loaded (user)) {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
+ manager->priv->new_users = g_slist_remove (manager->priv->new_users,
+ user);
+
+ username = gdm_user_get_user_name (user);
+
+ if (username == NULL) {
+ const char *object_path;
+
+ object_path = gdm_user_get_object_path (user);
+
+ if (object_path != NULL) {
+ g_warning ("GdmUserManager: user has no username "
+ "(object path: %s, uid: %lu)",
+ object_path, gdm_user_get_uid (user));
+ } else {
+ g_warning ("GdmUserManager: user has no username (uid: %lu)",
+ gdm_user_get_uid (user));
+ }
+ g_object_unref (user);
+ return;
+ }
- g_debug ("Creating new user");
+ if (username_in_exclude_list (manager, username)) {
+ g_debug ("GdmUserManager: excluding user '%s'", username);
+ g_object_unref (user);
+ return;
+ }
- user = create_user (manager);
- _gdm_user_update (user, pwent);
+ old_user = g_hash_table_lookup (manager->priv->users_by_name, username);
+
+ /* If username got added earlier by a different means, trump it now.
+ */
+ if (old_user != NULL) {
+ remove_user (manager, old_user);
+ }
add_user (manager, user);
+ g_object_unref (user);
+
+ if (manager->priv->new_users == NULL) {
+ set_is_loaded (manager, TRUE);
+ }
+}
+
+static GdmUser *
+add_new_user_for_object_path (const char *object_path,
+ GdmUserManager *manager)
+{
+ GdmUser *user;
+
+ user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path);
+
+ if (user != NULL) {
+ return user;
+ }
+ user = create_new_user (manager);
+ _gdm_user_update_from_object_path (user, object_path);
return user;
}
-static char *
-get_current_seat_id (DBusGConnection *connection)
+static void
+on_new_user_in_accounts_service (DBusGProxy *proxy,
+ const char *object_path,
+ gpointer user_data)
{
- DBusGProxy *proxy;
- GError *error;
- char *session_id;
- char *seat_id;
- gboolean res;
+ GdmUserManager *manager = GDM_USER_MANAGER (user_data);
+
+ add_new_user_for_object_path (object_path, manager);
+}
+
+static void
+on_user_removed_in_accounts_service (DBusGProxy *proxy,
+ const char *object_path,
+ gpointer user_data)
+{
+ GdmUserManager *manager = GDM_USER_MANAGER (user_data);
+ GdmUser *user;
+
+ user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path);
- proxy = NULL;
+ manager->priv->new_users = g_slist_remove (manager->priv->new_users, user);
+
+ remove_user (manager, user);
+}
+
+static void
+on_get_current_session_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ GdmUserManager *manager)
+{
+ GError *error;
+ char *session_id;
+ gboolean res;
+
+ g_assert (manager->priv->seat.get_current_session_call == call);
+ g_assert (manager->priv->seat.state == GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID);
+
+ error = NULL;
session_id = NULL;
- seat_id = NULL;
+ res = dbus_g_proxy_end_call (proxy,
+ call,
+ &error,
+ DBUS_TYPE_G_OBJECT_PATH,
+ &session_id,
+ G_TYPE_INVALID);
+ manager->priv->seat.get_current_session_call = NULL;
+ g_object_unref (proxy);
+
+ if (! res) {
+ if (error != NULL) {
+ g_debug ("Failed to identify the current session: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("Failed to identify the current session");
+ }
+ unload_seat (manager);
+ maybe_set_is_loaded (manager);
+ return;
+ }
- proxy = dbus_g_proxy_new_for_name (connection,
+ manager->priv->seat.session_id = session_id;
+ manager->priv->seat.state++;
+
+ load_seat_incrementally (manager);
+}
+
+static void
+get_current_session_id (GdmUserManager *manager)
+{
+ DBusGProxy *proxy;
+ DBusGProxyCall *call;
+
+ proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
CK_NAME,
CK_MANAGER_PATH,
CK_MANAGER_INTERFACE);
if (proxy == NULL) {
g_warning ("Failed to connect to the ConsoleKit manager object");
- goto out;
+ goto failed;
}
- error = NULL;
- res = dbus_g_proxy_call (proxy,
- "GetCurrentSession",
- &error,
- G_TYPE_INVALID,
- DBUS_TYPE_G_OBJECT_PATH,
- &session_id,
- G_TYPE_INVALID);
- if (! res) {
- g_debug ("Failed to identify the current session: %s", error->message);
- g_error_free (error);
- goto out;
+ call = dbus_g_proxy_begin_call (proxy,
+ "GetCurrentSession",
+ (DBusGProxyCallNotify)
+ on_get_current_session_finished,
+ manager,
+ NULL,
+ G_TYPE_INVALID);
+ if (call == NULL) {
+ g_warning ("GdmUserManager: failed to make GetCurrentSession call");
+ goto failed;
}
- seat_id = get_seat_id_for_session (connection, session_id);
+ manager->priv->seat.get_current_session_call = call;
- out:
+ return;
+
+failed:
if (proxy != NULL) {
g_object_unref (proxy);
}
- g_free (session_id);
- return seat_id;
+ unload_seat (manager);
}
-static gboolean
-get_uid_from_session_id (GdmUserManager *manager,
- const char *session_id,
- uid_t *uidp)
+static void
+unload_new_session (GdmUserManagerNewSession *new_session)
{
- DBusGProxy *proxy;
- GError *error;
- guint uid;
- gboolean res;
+ GdmUserManager *manager;
+
+ manager = new_session->manager;
+
+ manager->priv->new_sessions = g_slist_remove (manager->priv->new_sessions,
+ new_session);
+
+ if (new_session->proxy != NULL) {
+ g_object_unref (new_session->proxy);
+ }
+
+ g_free (new_session->x11_display);
+ g_free (new_session->id);
+
+ g_slice_free (GdmUserManagerNewSession, new_session);
+}
+
+static void
+get_proxy_for_new_session (GdmUserManagerNewSession *new_session)
+{
+ GdmUserManager *manager;
+ DBusGProxy *proxy;
+
+ manager = new_session->manager;
proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
CK_NAME,
- session_id,
+ new_session->id,
CK_SESSION_INTERFACE);
if (proxy == NULL) {
- g_warning ("Failed to connect to the ConsoleKit session object");
- return FALSE;
+ g_warning ("Failed to connect to the ConsoleKit '%s' object",
+ new_session->id);
+ unload_new_session (new_session);
+ return;
}
+ new_session->proxy = proxy;
+ new_session->state++;
+
+ load_new_session_incrementally (new_session);
+}
+
+static void
+on_get_unix_user_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ GdmUserManagerNewSession *new_session)
+{
+ GdmUserManager *manager;
+ GError *error;
+ guint uid;
+ gboolean res;
+
+ manager = new_session->manager;
+
+ g_assert (new_session->get_unix_user_call == call);
+
error = NULL;
- res = dbus_g_proxy_call (proxy,
- "GetUnixUser",
- &error,
- G_TYPE_INVALID,
- G_TYPE_UINT, &uid,
- G_TYPE_INVALID);
- g_object_unref (proxy);
+
+ uid = (guint) -1;
+ res = dbus_g_proxy_end_call (proxy,
+ call,
+ &error,
+ G_TYPE_UINT, &uid,
+ G_TYPE_INVALID);
+ new_session->get_unix_user_call = NULL;
if (! res) {
- g_warning ("Failed to query the session: %s", error->message);
- g_error_free (error);
- return FALSE;
+ if (error != NULL) {
+ g_debug ("Failed to get uid of session '%s': %s",
+ new_session->id, error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("Failed to get uid of session '%s'",
+ new_session->id);
+ }
+ unload_new_session (new_session);
+ return;
}
- if (uidp != NULL) {
- *uidp = (uid_t) uid;
+ g_debug ("GdmUserManager: Found uid of session '%s': %u",
+ new_session->id, uid);
+
+ new_session->uid = (uid_t) uid;
+ new_session->state++;
+
+ load_new_session_incrementally (new_session);
+}
+
+static void
+get_uid_for_new_session (GdmUserManagerNewSession *new_session)
+{
+ DBusGProxyCall *call;
+
+ g_assert (new_session->proxy != NULL);
+
+ call = dbus_g_proxy_begin_call (new_session->proxy,
+ "GetUnixUser",
+ (DBusGProxyCallNotify)
+ on_get_unix_user_finished,
+ new_session,
+ NULL,
+ G_TYPE_INVALID);
+ if (call == NULL) {
+ g_warning ("GdmUserManager: failed to make GetUnixUser call");
+ goto failed;
}
- return TRUE;
+ new_session->get_unix_user_call = call;
+ return;
+
+failed:
+ unload_new_session (new_session);
}
static void
-seat_session_added (DBusGProxy *seat_proxy,
- const char *session_id,
- GdmUserManager *manager)
+on_find_user_by_name_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ GdmUserManagerFetchUserRequest *request)
{
- uid_t uid;
- gboolean res;
- struct passwd *pwent;
- GdmUser *user;
- gboolean is_new;
+ GdmUserManager *manager;
+ GError *error;
+ char *object_path;
+ gboolean res;
+
+ g_assert (request->call == call);
+
+ error = NULL;
+ object_path = NULL;
+ manager = request->manager;
+ res = dbus_g_proxy_end_call (manager->priv->accounts_proxy,
+ call,
+ &error,
+ DBUS_TYPE_G_OBJECT_PATH,
+ &object_path,
+ G_TYPE_INVALID);
+ if (! res) {
+ if (error != NULL) {
+ g_debug ("GdmUserManager: Failed to find user %s: %s",
+ request->username, error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("GdmUserManager: Failed to find user %s",
+ request->username);
+ }
+ give_up_and_fetch_user_locally (manager, request);
+ return;
+ }
+
+ g_debug ("GdmUserManager: Found object path of user '%s': %s",
+ request->username, object_path);
+ request->object_path = object_path;
+ request->state++;
+
+ fetch_user_incrementally (request);
+}
+
+static void
+find_user_in_accounts_service (GdmUserManager *manager,
+ GdmUserManagerFetchUserRequest *request)
+{
+ DBusGProxyCall *call;
+
+ g_debug ("GdmUserManager: Looking for user %s in accounts service",
+ request->username);
+
+ g_assert (manager->priv->accounts_proxy != NULL);
+
+ call = dbus_g_proxy_begin_call (manager->priv->accounts_proxy,
+ "FindUserByName",
+ (DBusGProxyCallNotify)
+ on_find_user_by_name_finished,
+ request,
+ NULL,
+ G_TYPE_STRING,
+ request->username,
+ G_TYPE_INVALID);
+
+ if (call == NULL) {
+ g_warning ("GdmUserManager: failed to make FindUserByName('%s') call",
+ request->username);
+ goto failed;
+ }
+
+ request->call = call;
+ return;
+
+failed:
+ give_up_and_fetch_user_locally (manager, request);
+}
+
+static void
+set_is_loaded (GdmUserManager *manager,
+ gboolean is_loaded)
+{
+ if (manager->priv->is_loaded != is_loaded) {
+ manager->priv->is_loaded = is_loaded;
+ g_object_notify (G_OBJECT (manager), "is-loaded");
+ }
+}
+
+static void
+on_list_cached_users_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call_id,
+ gpointer data)
+{
+ GdmUserManager *manager = data;
+ GError *error = NULL;
+ GPtrArray *paths;
+
+ manager->priv->listing_cached_users = FALSE;
+ if (!dbus_g_proxy_end_call (proxy,
+ call_id,
+ &error,
+ dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &paths,
+ G_TYPE_INVALID)) {
+ g_debug ("GdmUserManager: ListCachedUsers failed: %s", error->message);
+ g_error_free (error);
+
+ g_object_unref (manager->priv->accounts_proxy);
+ manager->priv->accounts_proxy = NULL;
+
+ load_users_manually (manager);
+
+ return;
+ }
+
+ g_ptr_array_foreach (paths, (GFunc)add_new_user_for_object_path, manager);
+
+ g_ptr_array_foreach (paths, (GFunc)g_free, NULL);
+ g_ptr_array_free (paths, TRUE);
+
+ /* Add users who are specifically included */
+ if (manager->priv->include_usernames != NULL) {
+ GSList *l;
+
+ for (l = manager->priv->include_usernames; l != NULL; l = l->next) {
+ GdmUser *user;
+
+ g_debug ("GdmUserManager: Adding included user %s", (char *)l->data);
+ /*
+ * The call to gdm_user_manager_get_user will add the user if it is
+ * valid and not already in the hash.
+ */
+ user = gdm_user_manager_get_user (manager, l->data);
+ if (user == NULL) {
+ g_debug ("GdmUserManager: unable to lookup user '%s'", (char *)l->data);
+ }
+ }
+ }
+}
- g_debug ("Session added: %s", session_id);
+static void
+on_get_x11_display_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ GdmUserManagerNewSession *new_session)
+{
+ GError *error;
+ char *x11_display;
+ gboolean res;
+
+ g_assert (new_session->get_x11_display_call == call);
+
+ error = NULL;
+ x11_display = NULL;
+ res = dbus_g_proxy_end_call (proxy,
+ call,
+ &error,
+ G_TYPE_STRING,
+ &x11_display,
+ G_TYPE_INVALID);
+ new_session->get_x11_display_call = NULL;
- res = get_uid_from_session_id (manager, session_id, &uid);
if (! res) {
- g_warning ("Unable to lookup user for session");
+ if (error != NULL) {
+ g_debug ("Failed to get the x11 display of session '%s': %s",
+ new_session->id, error->message);
+ g_error_free (error);
+ } else {
+ g_debug ("Failed to get the x11 display of session '%s'",
+ new_session->id);
+ }
+ unload_new_session (new_session);
return;
}
+ g_debug ("GdmUserManager: Found x11 display of session '%s': %s",
+ new_session->id, x11_display);
+
+ new_session->x11_display = x11_display;
+ new_session->state++;
+
+ load_new_session_incrementally (new_session);
+}
+
+static void
+get_x11_display_for_new_session (GdmUserManagerNewSession *new_session)
+{
+ DBusGProxyCall *call;
+
+ g_assert (new_session->proxy != NULL);
+
+ call = dbus_g_proxy_begin_call (new_session->proxy,
+ "GetX11Display",
+ (DBusGProxyCallNotify)
+ on_get_x11_display_finished,
+ new_session,
+ NULL,
+ G_TYPE_INVALID);
+ if (call == NULL) {
+ g_warning ("GdmUserManager: failed to make GetX11Display call");
+ goto failed;
+ }
+
+ new_session->get_x11_display_call = call;
+ return;
+
+failed:
+ unload_new_session (new_session);
+}
+
+static gboolean
+get_pwent_for_name (const char *name,
+ struct passwd **pwentp)
+{
+ struct passwd *pwent;
+
+ do {
+ errno = 0;
+ pwent = getpwnam (name);
+ } while (pwent == NULL && errno == EINTR);
+
+ if (pwentp != NULL) {
+ *pwentp = pwent;
+ }
+
+ return (pwent != NULL);
+}
+
+static gboolean
+get_pwent_for_uid (uid_t uid,
+ struct passwd **pwentp)
+{
+ struct passwd *pwent;
+
+ do {
+ errno = 0;
+ pwent = getpwuid (uid);
+ } while (pwent == NULL && errno == EINTR);
+
+ if (pwentp != NULL) {
+ *pwentp = pwent;
+ }
+
+ return (pwent != NULL);
+}
+
+static void
+maybe_add_new_session (GdmUserManagerNewSession *new_session)
+{
+ GdmUserManager *manager;
+ struct passwd *pwent;
+ GdmUser *user;
+
+ manager = GDM_USER_MANAGER (new_session->manager);
+
errno = 0;
- pwent = getpwuid (uid);
+ get_pwent_for_uid (new_session->uid, &pwent);
if (pwent == NULL) {
- g_warning ("Unable to lookup user id %d: %s", (int)uid, g_strerror (errno));
- return;
+ g_warning ("Unable to lookup user ID %d: %s",
+ (int) new_session->uid, g_strerror (errno));
+ goto failed;
}
/* check exclusions up front */
- if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
+ if (username_in_exclude_list (manager, pwent->pw_name)) {
g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
+ goto failed;
+ }
+
+ user = gdm_user_manager_get_user (manager, pwent->pw_name);
+ if (user == NULL) {
return;
}
- user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
- if (user == NULL) {
- g_debug ("Creating new user");
+ add_session_for_user (manager, user, new_session->id);
+
+ /* if we haven't yet gotten the login frequency
+ then at least add one because the session exists */
+ if (gdm_user_get_login_frequency (user) == 0) {
+ _gdm_user_update_login_frequency (user, 1);
+ }
+
+ manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_LOADED;
+ unload_new_session (new_session);
+ return;
+
+failed:
+ unload_new_session (new_session);
+}
+
+static void
+load_new_session (GdmUserManager *manager,
+ const char *session_id)
+{
+ GdmUserManagerNewSession *new_session;
+
+ new_session = g_slice_new0 (GdmUserManagerNewSession);
+
+ new_session->manager = manager;
+ new_session->id = g_strdup (session_id);
+ new_session->state = GDM_USER_MANAGER_NEW_SESSION_STATE_UNLOADED + 1;
+
+ manager->priv->new_sessions = g_slist_prepend (manager->priv->new_sessions,
+ new_session);
+ load_new_session_incrementally (new_session);
+}
+
+static void
+seat_session_added (DBusGProxy *seat_proxy,
+ const char *session_id,
+ GdmUserManager *manager)
+{
+ g_debug ("GdmUserManager: Session added: %s", session_id);
+
+ load_new_session (manager, session_id);
+}
- user = create_user (manager);
- _gdm_user_update (user, pwent);
- is_new = TRUE;
- } else {
- is_new = FALSE;
- }
+static gint
+match_new_session_cmpfunc (gconstpointer a,
+ gconstpointer b)
+{
+ GdmUserManagerNewSession *new_session;
+ const char *session_id;
- res = maybe_add_session_for_user (manager, user, session_id);
+ new_session = (GdmUserManagerNewSession *) a;
+ session_id = (const char *) b;
- /* only add the user if we added a session */
- if (is_new) {
- if (res) {
- add_user (manager, user);
- } else {
- g_object_unref (user);
- }
- }
+ return strcmp (new_session->id, session_id);
}
static void
const char *session_id,
GdmUserManager *manager)
{
- GdmUser *user;
- char *username;
+ GdmUser *user;
+ GSList *found;
+ char *username;
+
+ g_debug ("GdmUserManager: Session removed: %s", session_id);
+
+ found = g_slist_find_custom (manager->priv->new_sessions,
+ session_id,
+ match_new_session_cmpfunc);
+
+ if (found != NULL) {
+ GdmUserManagerNewSession *new_session;
- g_debug ("Session removed: %s", session_id);
+ new_session = (GdmUserManagerNewSession *) found->data;
+
+ if (new_session->state > GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY) {
+ g_debug ("GdmUserManager: New session for uid %d on "
+ "x11 display %s removed before fully loading",
+ (int) new_session->uid, new_session->x11_display);
+ } else if (new_session->state > GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID) {
+ g_debug ("GdmUserManager: New session for uid %d "
+ "removed before fully loading",
+ (int) new_session->uid);
+ } else {
+ g_debug ("GdmUserManager: New session removed "
+ "before fully loading");
+ }
+ unload_new_session (new_session);
+ return;
+ }
/* since the session object may already be gone
* we can't query CK directly */
return;
}
- user = g_hash_table_lookup (manager->priv->users, username);
+ user = g_hash_table_lookup (manager->priv->users_by_name, username);
if (user == NULL) {
/* nothing to do */
return;
}
static void
-on_proxy_destroy (DBusGProxy *proxy,
- GdmUserManager *manager)
+on_seat_proxy_destroy (DBusGProxy *proxy,
+ GdmUserManager *manager)
{
g_debug ("GdmUserManager: seat proxy destroyed");
- manager->priv->seat_proxy = NULL;
+ manager->priv->seat.proxy = NULL;
}
static void
DBusGProxy *proxy;
GError *error;
- g_assert (manager->priv->seat_proxy == NULL);
-
- error = NULL;
- manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
- if (manager->priv->connection == NULL) {
- g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
- g_error_free (error);
- return;
- }
-
- manager->priv->seat_id = get_current_seat_id (manager->priv->connection);
- if (manager->priv->seat_id == NULL) {
- return;
- }
-
- g_debug ("GdmUserManager: Found current seat: %s", manager->priv->seat_id);
+ g_assert (manager->priv->seat.proxy == NULL);
error = NULL;
proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
CK_NAME,
- manager->priv->seat_id,
+ manager->priv->seat.id,
CK_SEAT_INTERFACE,
&error);
if (proxy == NULL) {
- g_warning ("Failed to connect to the ConsoleKit seat object: %s", error->message);
- g_error_free (error);
+ if (error != NULL) {
+ g_warning ("Failed to connect to the ConsoleKit seat object: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Failed to connect to the ConsoleKit seat object");
+ }
+ unload_seat (manager);
return;
}
- g_signal_connect (proxy, "destroy", G_CALLBACK (on_proxy_destroy), manager);
+ g_signal_connect (proxy, "destroy", G_CALLBACK (on_seat_proxy_destroy), manager);
dbus_g_proxy_add_signal (proxy,
"SessionAdded",
G_CALLBACK (seat_session_removed),
manager,
NULL);
- manager->priv->seat_proxy = proxy;
+ manager->priv->seat.proxy = proxy;
+ manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_LOADED;
+}
+
+static void
+unload_seat (GdmUserManager *manager)
+{
+ manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED;
+
+ if (manager->priv->seat.proxy != NULL) {
+ g_object_unref (manager->priv->seat.proxy);
+ manager->priv->seat.proxy = NULL;
+ }
+
+ g_free (manager->priv->seat.id);
+ manager->priv->seat.id = NULL;
+
+ g_free (manager->priv->seat.session_id);
+ manager->priv->seat.session_id = NULL;
+}
+
+static void
+get_accounts_proxy (GdmUserManager *manager)
+{
+ DBusGProxy *proxy;
+ GError *error;
+
+ g_assert (manager->priv->accounts_proxy == NULL);
+
+ error = NULL;
+ proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+ ACCOUNTS_NAME,
+ ACCOUNTS_PATH,
+ ACCOUNTS_INTERFACE);
+ manager->priv->accounts_proxy = proxy;
+
+ dbus_g_proxy_add_signal (proxy,
+ "UserAdded",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (proxy,
+ "UserDeleted",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (proxy,
+ "UserAdded",
+ G_CALLBACK (on_new_user_in_accounts_service),
+ manager,
+ NULL);
+ dbus_g_proxy_connect_signal (proxy,
+ "UserDeleted",
+ G_CALLBACK (on_user_removed_in_accounts_service),
+ manager,
+ NULL);
+}
+
+static void
+load_new_session_incrementally (GdmUserManagerNewSession *new_session)
+{
+ switch (new_session->state) {
+ case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_PROXY:
+ get_proxy_for_new_session (new_session);
+ break;
+ case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID:
+ get_uid_for_new_session (new_session);
+ break;
+ case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY:
+ get_x11_display_for_new_session (new_session);
+ break;
+ case GDM_USER_MANAGER_NEW_SESSION_STATE_MAYBE_ADD:
+ maybe_add_new_session (new_session);
+ break;
+ case GDM_USER_MANAGER_NEW_SESSION_STATE_LOADED:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+free_fetch_user_request (GdmUserManagerFetchUserRequest *request)
+{
+ GdmUserManager *manager;
+
+ manager = request->manager;
+
+ manager->priv->fetch_user_requests = g_slist_remove (manager->priv->fetch_user_requests, request);
+ g_free (request->username);
+ g_free (request->object_path);
+ g_slice_free (GdmUserManagerFetchUserRequest, request);
+}
+
+static void
+give_up_and_fetch_user_locally (GdmUserManager *manager,
+ GdmUserManagerFetchUserRequest *request)
+{
+
+ g_debug ("GdmUserManager: account service unavailable, "
+ "fetching user %s locally",
+ request->username);
+ fetch_user_locally (manager, request->user, request->username);
+ request->state = GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED;
+}
+
+static void
+on_user_manager_maybe_ready_for_request (GdmUserManager *manager,
+ GParamSpec *pspec,
+ GdmUserManagerFetchUserRequest *request)
+{
+ if (!manager->priv->is_loaded) {
+ return;
+ }
+
+ g_signal_handlers_disconnect_by_func (manager, on_user_manager_maybe_ready_for_request, request);
+
+ request->state++;
+ fetch_user_incrementally (request);
+}
+static void
+fetch_user_incrementally (GdmUserManagerFetchUserRequest *request)
+{
+ GdmUserManager *manager;
+
+ g_debug ("GdmUserManager: finding user %s state %d",
+ request->username, request->state);
+ manager = request->manager;
+ switch (request->state) {
+ case GDM_USER_MANAGER_GET_USER_STATE_WAIT_FOR_LOADED:
+ if (manager->priv->is_loaded) {
+ request->state++;
+ fetch_user_incrementally (request);
+ } else {
+ g_debug ("GdmUserManager: waiting for user manager to load before finding user %s",
+ request->username);
+ g_signal_connect (manager, "notify::is-loaded",
+ G_CALLBACK (on_user_manager_maybe_ready_for_request), request);
+
+ }
+ break;
+
+ case GDM_USER_MANAGER_GET_USER_STATE_ASK_ACCOUNTS_SERVICE:
+ if (manager->priv->accounts_proxy == NULL) {
+ give_up_and_fetch_user_locally (manager, request);
+ } else {
+ find_user_in_accounts_service (manager, request);
+ }
+ break;
+ case GDM_USER_MANAGER_GET_USER_STATE_FETCHED:
+ g_debug ("GdmUserManager: user %s fetched", request->username);
+ _gdm_user_update_from_object_path (request->user, request->object_path);
+ break;
+ case GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED:
+ g_debug ("GdmUserManager: user %s was not fetched", request->username);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (request->state == GDM_USER_MANAGER_GET_USER_STATE_FETCHED ||
+ request->state == GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED) {
+ g_debug ("GdmUserManager: finished handling request for user %s",
+ request->username);
+ free_fetch_user_request (request);
+ }
+}
+
+static void
+fetch_user_from_accounts_service (GdmUserManager *manager,
+ GdmUser *user,
+ const char *username)
+{
+ GdmUserManagerFetchUserRequest *request;
+
+ request = g_slice_new0 (GdmUserManagerFetchUserRequest);
+
+ request->manager = manager;
+ request->username = g_strdup (username);
+ request->user = user;
+ request->state = GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED + 1;
+
+ manager->priv->fetch_user_requests = g_slist_prepend (manager->priv->fetch_user_requests,
+ request);
+ fetch_user_incrementally (request);
+}
+
+static void
+fetch_user_locally (GdmUserManager *manager,
+ GdmUser *user,
+ const char *username)
+{
+ struct passwd *pwent;
+
+ get_pwent_for_name (username, &pwent);
+
+ if (pwent != NULL) {
+ _gdm_user_update_from_pwent (user, pwent);
+ }
}
/**
* @manager: the manager to query.
* @username: the login name of the user to get.
*
- * Retrieves a pointer to the #GdmUser object for the login named @username
- * from @manager. This pointer is not a reference, and should not be released.
+ * Retrieves a pointer to the #GdmUser object for the login @username
+ * from @manager. Trying to use this object before its
+ * #GdmUser:is-loaded property is %TRUE will result in undefined
+ * behavior.
*
- * Returns: (transfer none): a pointer to a #GdmUser object.
+ * Returns: (transfer none): #GdmUser object
**/
GdmUser *
gdm_user_manager_get_user (GdmUserManager *manager,
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
- user = g_hash_table_lookup (manager->priv->users, username);
+ user = g_hash_table_lookup (manager->priv->users_by_name, username);
+ /* if we don't have it loaded try to load it now */
if (user == NULL) {
- struct passwd *pwent;
-
- pwent = getpwnam (username);
+ user = create_new_user (manager);
- if (pwent != NULL) {
- user = add_new_user_for_pwent (manager, pwent);
+ if (manager->priv->accounts_proxy != NULL) {
+ fetch_user_from_accounts_service (manager, user, username);
+ } else {
+ fetch_user_locally (manager, user, username);
}
}
return user;
}
-
/**
* gdm_user_manager_get_user_by_uid:
* @manager: the manager to query.
- * @uid: the user id
+ * @uid: the uid of the user to get.
*
- * Retrieves a pointer to the #GdmUser object for the login named @username
- * from @manager.
+ * Retrieves a pointer to the #GdmUser object for the uid @uid
+ * from @manager. Trying to use this object before its
+ * #GdmUser:is-loaded property is %TRUE will result in undefined
+ * behavior.
*
- * Returns: (transfer none): a pointer to a #GdmUser object.
- **/
+ * Returns: (transfer none): #GdmUser object
+ */
GdmUser *
gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
gulong uid)
{
- GdmUser *user;
struct passwd *pwent;
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
- pwent = getpwuid (uid);
+ get_pwent_for_uid (uid, &pwent);
if (pwent == NULL) {
g_warning ("GdmUserManager: unable to lookup uid %d", (int)uid);
return NULL;
}
- user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
-
- if (user == NULL) {
- user = add_new_user_for_pwent (manager, pwent);
- }
-
- return user;
+ return gdm_user_manager_get_user (manager, pwent->pw_name);
}
static void
/**
* gdm_user_manager_list_users:
+ * @manager: a #GdmUserManager
*
- * Return value: (element-type GdmUser) (transfer container): list of #GdmUser elements
+ * Get a list of system user accounts
+ *
+ * Returns: (element-type GdmUser) (transfer full): List of #GdmUser objects
*/
GSList *
gdm_user_manager_list_users (GdmUserManager *manager)
g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
retval = NULL;
- g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &retval);
+ g_hash_table_foreach (manager->priv->users_by_name, listify_hash_values_hfunc, &retval);
return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
}
error = NULL;
re = g_regex_new ("(?P<username>[0-9a-zA-Z]+)[ ]+(?P<frequency>[0-9]+)", 0, 0, &error);
if (re == NULL) {
- g_critical ("%s", error->message);
+ if (error != NULL) {
+ g_critical ("%s", error->message);
+ } else {
+ g_critical ("Error in regex call");
+ }
goto out;
}
return;
}
- if (g_hash_table_lookup (manager->priv->exclusions, username)) {
+ if (username_in_exclude_list (manager, username)) {
g_debug ("GdmUserManager: excluding user '%s'", username);
g_free (username);
return;
return;
}
- g_object_set (user, "login-frequency", frequency, NULL);
- g_signal_emit (manager, signals [USER_LOGIN_FREQUENCY_CHANGED], 0, user);
+ _gdm_user_update_login_frequency (user, frequency);
g_free (username);
}
+static void
+maybe_set_is_loaded (GdmUserManager *manager)
+{
+ if (manager->priv->is_loaded) {
+ return;
+ }
+
+ if (manager->priv->ck_history_pid != 0) {
+ return;
+ }
+
+ if (manager->priv->load_passwd_pending) {
+ return;
+ }
+
+ if (manager->priv->get_sessions_call != NULL) {
+ return;
+ }
+
+ if (manager->priv->listing_cached_users) {
+ return;
+ }
+
+ /* Don't set is_loaded yet unless the seat is already loaded
+ * or failed to load.
+ */
+ if (manager->priv->seat.state != GDM_USER_MANAGER_SEAT_STATE_LOADED
+ && manager->priv->seat.state != GDM_USER_MANAGER_SEAT_STATE_UNLOADED) {
+ return;
+ }
+
+ set_is_loaded (manager, TRUE);
+}
+
static gboolean
ck_history_watch (GIOChannel *source,
GIOCondition condition,
}
if (done) {
- g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
-
manager->priv->ck_history_id = 0;
+ if (manager->priv->ck_history_watchdog_id != 0) {
+ g_source_remove (manager->priv->ck_history_watchdog_id);
+ manager->priv->ck_history_watchdog_id = 0;
+ }
+ manager->priv->ck_history_pid = 0;
+
+ maybe_set_is_loaded (manager);
+
return FALSE;
}
return TRUE;
}
-static void
-reload_ck_history (GdmUserManager *manager)
+static int
+signal_pid (int pid,
+ int signal)
+{
+ int status = -1;
+
+ status = kill (pid, signal);
+
+ if (status < 0) {
+ if (errno == ESRCH) {
+ g_debug ("Child process %lu was already dead.",
+ (unsigned long) pid);
+ } else {
+ char buf [1024];
+ snprintf (buf,
+ sizeof (buf),
+ "Couldn't kill child process %lu",
+ (unsigned long) pid);
+ perror (buf);
+ }
+ }
+
+ return status;
+}
+
+static gboolean
+ck_history_watchdog (GdmUserManager *manager)
+{
+ if (manager->priv->ck_history_pid > 0) {
+ g_debug ("Killing ck-history process");
+ signal_pid (manager->priv->ck_history_pid, SIGTERM);
+ manager->priv->ck_history_pid = 0;
+ }
+
+ manager->priv->ck_history_watchdog_id = 0;
+ return FALSE;
+}
+
+static gboolean
+load_ck_history (GdmUserManager *manager)
{
char *command;
+ char *since;
const char *seat_id;
GError *error;
gboolean res;
char **argv;
int standard_out;
GIOChannel *channel;
+ GTimeVal tv;
+
+ g_assert (manager->priv->ck_history_id == 0);
+
+ command = NULL;
seat_id = NULL;
- if (manager->priv->seat_id != NULL
- && g_str_has_prefix (manager->priv->seat_id, "/org/freedesktop/ConsoleKit/")) {
+ if (manager->priv->seat.id != NULL
+ && g_str_has_prefix (manager->priv->seat.id, "/org/freedesktop/ConsoleKit/")) {
- seat_id = manager->priv->seat_id + strlen ("/org/freedesktop/ConsoleKit/");
+ seat_id = manager->priv->seat.id + strlen ("/org/freedesktop/ConsoleKit/");
}
if (seat_id == NULL) {
- g_debug ("Unable to find users: no seat-id found");
- return;
+ g_warning ("Unable to load CK history: no seat-id found");
+ goto out;
}
- command = g_strdup_printf ("ck-history --frequent --seat='%s' --session-type=''",
+ g_get_current_time (&tv);
+ tv.tv_sec -= LOGIN_FREQUENCY_TIME_WINDOW_SECS;
+ since = g_time_val_to_iso8601 (&tv);
+
+ command = g_strdup_printf ("ck-history --frequent --since='%s' --seat='%s' --session-type=''",
+ since,
seat_id);
+ g_free (since);
g_debug ("GdmUserManager: running '%s'", command);
error = NULL;
if (! g_shell_parse_argv (command, NULL, &argv, &error)) {
- g_warning ("Could not parse command: %s", error->message);
- g_error_free (error);
+ if (error != NULL) {
+ g_warning ("Could not parse command: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Could not parse command");
+ }
goto out;
}
G_SPAWN_SEARCH_PATH,
NULL,
NULL,
- NULL, /* pid */
+ &manager->priv->ck_history_pid, /* pid */
NULL,
&standard_out,
NULL,
&error);
g_strfreev (argv);
if (! res) {
- g_warning ("Unable to run ck-history: %s", error->message);
- g_error_free (error);
+ if (error != NULL) {
+ g_warning ("Unable to run ck-history: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Unable to run ck-history");
+ }
goto out;
}
g_io_channel_set_flags (channel,
g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
NULL);
+ manager->priv->ck_history_watchdog_id = g_timeout_add_seconds (1, (GSourceFunc) ck_history_watchdog, manager);
manager->priv->ck_history_id = g_io_add_watch (channel,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
(GIOFunc)ck_history_watch,
g_io_channel_unref (channel);
out:
+
g_free (command);
+
+ return manager->priv->ck_history_id != 0;
}
static void
-reload_passwd (GdmUserManager *manager)
+reload_passwd_file (GHashTable *valid_shells,
+ GSList *exclude_users,
+ GSList *include_users,
+ gboolean include_all,
+ GHashTable *current_users_by_name,
+ GSList **added_users,
+ GSList **removed_users)
{
- struct passwd *pwent;
- GSList *old_users;
- GSList *new_users;
- GSList *list;
- FILE *fp;
+ FILE *fp;
+ GHashTableIter iter;
+ GHashTable *new_users_by_name;
+ GdmUser *user;
+ char *name;
- old_users = NULL;
- new_users = NULL;
+ new_users_by_name = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ g_object_unref);
errno = 0;
fp = fopen (PATH_PASSWD, "r");
goto out;
}
- g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &old_users);
- g_slist_foreach (old_users, (GFunc) g_object_ref, NULL);
-
/* Make sure we keep users who are logged in no matter what. */
- for (list = old_users; list; list = list->next) {
- if (gdm_user_get_num_sessions (list->data) > 0) {
- g_object_freeze_notify (G_OBJECT (list->data));
- new_users = g_slist_prepend (new_users, g_object_ref (list->data));
+ g_hash_table_iter_init (&iter, current_users_by_name);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+ struct passwd *pwent;
+
+ get_pwent_for_name (name, &pwent);
+ if (pwent == NULL) {
+ continue;
+ }
+
+ g_object_freeze_notify (G_OBJECT (user));
+ _gdm_user_update_from_pwent (user, pwent);
+ g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), g_object_ref (user));
+ }
+
+ if (include_users != NULL) {
+ GSList *l;
+ for (l = include_users; l != NULL; l = l->next) {
+ struct passwd *pwent;
+
+ get_pwent_for_name (l->data, &pwent);
+ if (pwent == NULL) {
+ continue;
+ }
+
+ user = g_hash_table_lookup (new_users_by_name, pwent->pw_name);
+ if (user != NULL) {
+ /* already there */
+ continue;
+ }
+
+ user = g_hash_table_lookup (current_users_by_name, pwent->pw_name);
+ if (user == NULL) {
+ user = g_object_new (GDM_TYPE_USER, NULL);
+ } else {
+ g_object_ref (user);
+ }
+ g_object_freeze_notify (G_OBJECT (user));
+ _gdm_user_update_from_pwent (user, pwent);
+ g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), user);
}
}
- for (pwent = fgetpwent (fp); pwent != NULL; pwent = fgetpwent (fp)) {
- GdmUser *user;
+ if (include_all != TRUE) {
+ g_debug ("GdmUserManager: include_all is FALSE");
+ } else {
+ struct passwd *pwent;
- user = NULL;
+ g_debug ("GdmUserManager: include_all is TRUE");
+
+ for (pwent = fgetpwent (fp);
+ pwent != NULL;
+ pwent = fgetpwent (fp)) {
+
+ /* Skip users below MinimalUID... */
+ if (pwent->pw_uid < FALLBACK_MINIMAL_UID) {
+ continue;
+ }
+
+ /* ...And users w/ invalid shells... */
+ if (pwent->pw_shell == NULL
+ || !g_hash_table_lookup (valid_shells, pwent->pw_shell)) {
+ g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
+ continue;
+ }
+
+ /* always exclude the "gdm" user. */
+ if (strcmp (pwent->pw_name, GDM_USERNAME) == 0) {
+ continue;
+ }
+
+ /* ...And explicitly excluded users */
+ if (exclude_users != NULL) {
+ GSList *found;
+
+ found = g_slist_find_custom (exclude_users,
+ pwent->pw_name,
+ match_name_cmpfunc);
+ if (found != NULL) {
+ g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
+ continue;
+ }
+ }
+
+ user = g_hash_table_lookup (new_users_by_name, pwent->pw_name);
+ if (user != NULL) {
+ /* already there */
+ continue;
+ }
+
+ user = g_hash_table_lookup (current_users_by_name, pwent->pw_name);
+ if (user == NULL) {
+ user = g_object_new (GDM_TYPE_USER, NULL);
+ } else {
+ g_object_ref (user);
+ }
+
+ /* Freeze & update users not already in the new list */
+ g_object_freeze_notify (G_OBJECT (user));
+ _gdm_user_update_from_pwent (user, pwent);
+ g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), user);
+ }
+ }
- /* Skip users below MinimalUID... */
- if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
- continue;
+ /* Go through and handle added users */
+ g_hash_table_iter_init (&iter, new_users_by_name);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+ GdmUser *user2;
+ user2 = g_hash_table_lookup (current_users_by_name, name);
+ if (user2 == NULL) {
+ *added_users = g_slist_prepend (*added_users, g_object_ref (user));
}
+ }
- /* ...And users w/ invalid shells... */
- if (pwent->pw_shell == NULL ||
- !g_hash_table_lookup (manager->priv->shells, pwent->pw_shell)) {
- g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
- continue;
+ /* Go through and handle removed users */
+ g_hash_table_iter_init (&iter, current_users_by_name);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+ GdmUser *user2;
+ user2 = g_hash_table_lookup (new_users_by_name, name);
+ if (user2 == NULL) {
+ *removed_users = g_slist_prepend (*removed_users, g_object_ref (user));
}
+ }
- /* ...And explicitly excluded users */
- if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
- g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
- continue;
+ out:
+ /* Cleanup */
+
+ fclose (fp);
+
+ g_hash_table_iter_init (&iter, new_users_by_name);
+ while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+ g_object_thaw_notify (G_OBJECT (user));
+ }
+
+ g_hash_table_destroy (new_users_by_name);
+}
+
+typedef struct {
+ GdmUserManager *manager;
+ GSList *exclude_users;
+ GSList *include_users;
+ gboolean include_all;
+ GHashTable *shells;
+ GHashTable *current_users_by_name;
+ GSList *added_users;
+ GSList *removed_users;
+} PasswdData;
+
+static void
+passwd_data_free (PasswdData *data)
+{
+ if (data->manager != NULL) {
+ g_object_unref (data->manager);
+ }
+
+ g_slist_foreach (data->added_users, (GFunc) g_object_unref, NULL);
+ g_slist_free (data->added_users);
+
+ g_slist_foreach (data->removed_users, (GFunc) g_object_unref, NULL);
+ g_slist_free (data->removed_users);
+
+ g_slist_foreach (data->exclude_users, (GFunc) g_free, NULL);
+ g_slist_free (data->exclude_users);
+
+ g_slist_foreach (data->include_users, (GFunc) g_free, NULL);
+ g_slist_free (data->include_users);
+
+ g_slice_free (PasswdData, data);
+}
+
+static gboolean
+reload_passwd_job_done (PasswdData *data)
+{
+ GSList *l;
+
+ g_debug ("GdmUserManager: done reloading passwd file");
+
+ /* Go through and handle added users */
+ for (l = data->added_users; l != NULL; l = l->next) {
+ add_user (data->manager, l->data);
+ }
+
+ /* Go through and handle removed users */
+ for (l = data->removed_users; l != NULL; l = l->next) {
+ remove_user (data->manager, l->data);
+ }
+
+ data->manager->priv->load_passwd_pending = FALSE;
+
+ if (! data->manager->priv->is_loaded) {
+ maybe_set_is_loaded (data->manager);
+
+ if (data->manager->priv->include_all == TRUE) {
+ monitor_local_users (data->manager);
}
+ }
+
+ passwd_data_free (data);
+
+ return FALSE;
+}
- user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+static gboolean
+do_reload_passwd_job (GIOSchedulerJob *job,
+ GCancellable *cancellable,
+ PasswdData *data)
+{
+ g_debug ("GdmUserManager: reloading passwd file worker");
- /* Update users already in the *new* list */
- if (g_slist_find (new_users, user)) {
- _gdm_user_update (user, pwent);
- continue;
- }
+ reload_passwd_file (data->shells,
+ data->exclude_users,
+ data->include_users,
+ data->include_all,
+ data->current_users_by_name,
+ &data->added_users,
+ &data->removed_users);
- if (user == NULL) {
- user = create_user (manager);
- } else {
- g_object_ref (user);
- }
+ g_io_scheduler_job_send_to_mainloop_async (job,
+ (GSourceFunc) reload_passwd_job_done,
+ data,
+ NULL);
- /* Freeze & update users not already in the new list */
- g_object_freeze_notify (G_OBJECT (user));
- _gdm_user_update (user, pwent);
+ return FALSE;
+}
- new_users = g_slist_prepend (new_users, user);
- }
+static GSList *
+slist_deep_copy (const GSList *list)
+{
+ GSList *retval;
+ GSList *l;
- /* Go through and handle added users */
- for (list = new_users; list; list = list->next) {
- if (! g_slist_find (old_users, list->data)) {
- add_user (manager, list->data);
- }
- }
+ if (list == NULL)
+ return NULL;
- /* Go through and handle removed users */
- for (list = old_users; list; list = list->next) {
- if (! g_slist_find (new_users, list->data)) {
- g_signal_emit (manager, signals[USER_REMOVED], 0, list->data);
- g_hash_table_remove (manager->priv->users,
- gdm_user_get_user_name (list->data));
- }
+ retval = g_slist_copy ((GSList *) list);
+ for (l = retval; l != NULL; l = l->next) {
+ l->data = g_strdup (l->data);
}
- out:
- /* Cleanup */
-
- fclose (fp);
-
- g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL);
- g_slist_foreach (new_users, (GFunc) g_object_unref, NULL);
- g_slist_free (new_users);
+ return retval;
+}
- g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
- g_slist_free (old_users);
+static void
+schedule_reload_passwd (GdmUserManager *manager)
+{
+ PasswdData *passwd_data;
+
+ manager->priv->load_passwd_pending = TRUE;
+
+ passwd_data = g_slice_new0 (PasswdData);
+ passwd_data->manager = g_object_ref (manager);
+ passwd_data->shells = manager->priv->shells;
+ passwd_data->exclude_users = slist_deep_copy (manager->priv->exclude_usernames);
+ passwd_data->include_users = slist_deep_copy (manager->priv->include_usernames);
+ passwd_data->include_all = manager->priv->include_all;
+ passwd_data->current_users_by_name = manager->priv->users_by_name;
+ passwd_data->added_users = NULL;
+ passwd_data->removed_users = NULL;
+
+ g_debug ("GdmUserManager: scheduling a passwd file update");
+
+ g_io_scheduler_push_job ((GIOSchedulerJobFunc) do_reload_passwd_job,
+ passwd_data,
+ NULL,
+ G_PRIORITY_DEFAULT,
+ NULL);
}
static void
-reload_users (GdmUserManager *manager)
+load_sessions_from_array (GdmUserManager *manager,
+ const char * const *session_ids,
+ int number_of_sessions)
{
- reload_ck_history (manager);
- reload_passwd (manager);
+ int i;
+
+ for (i = 0; i < number_of_sessions; i++) {
+ load_new_session (manager, session_ids[i]);
+ }
}
-static gboolean
-reload_users_timeout (GdmUserManager *manager)
+static void
+on_get_sessions_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ GdmUserManager *manager)
{
- reload_users (manager);
- manager->priv->reload_id = 0;
+ GError *error;
+ gboolean res;
+ GPtrArray *sessions;
- return FALSE;
+ g_assert (manager->priv->get_sessions_call == call);
+
+ error = NULL;
+ sessions = NULL;
+ res = dbus_g_proxy_end_call (proxy,
+ call,
+ &error,
+ GDM_DBUS_TYPE_G_OBJECT_PATH_ARRAY,
+ &sessions,
+ G_TYPE_INVALID);
+
+ if (! res) {
+ if (error != NULL) {
+ g_warning ("unable to determine sessions for seat: %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("unable to determine sessions for seat");
+ }
+ return;
+ }
+
+ manager->priv->get_sessions_call = NULL;
+ g_assert (sessions->len <= G_MAXINT);
+ load_sessions_from_array (manager,
+ (const char * const *) sessions->pdata,
+ (int) sessions->len);
+ g_ptr_array_foreach (sessions, (GFunc) g_free, NULL);
+ g_ptr_array_free (sessions, TRUE);
+ maybe_set_is_loaded (manager);
}
static void
-queue_reload_users (GdmUserManager *manager)
+load_sessions (GdmUserManager *manager)
{
- if (manager->priv->reload_id > 0) {
+ DBusGProxyCall *call;
+
+ if (manager->priv->seat.proxy == NULL) {
+ g_debug ("GdmUserManager: no seat proxy; can't load sessions");
+ return;
+ }
+
+ call = dbus_g_proxy_begin_call (manager->priv->seat.proxy,
+ "GetSessions",
+ (DBusGProxyCallNotify)
+ on_get_sessions_finished,
+ manager,
+ NULL,
+ G_TYPE_INVALID);
+
+ if (call == NULL) {
+ g_warning ("GdmUserManager: failed to make GetSessions call");
return;
}
- g_signal_emit (G_OBJECT (manager), signals[LOADING_USERS], 0);
- manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, manager);
+ manager->priv->get_sessions_call = call;
}
static void
endusershell ();
}
+static void
+load_users_manually (GdmUserManager *manager)
+{
+ gboolean res;
+
+ manager->priv->shells = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ reload_shells (manager);
+
+ load_sessions (manager);
+
+ res = load_ck_history (manager);
+ schedule_reload_passwd (manager);
+}
+
+static void
+load_users (GdmUserManager *manager)
+{
+ g_assert (manager->priv->accounts_proxy != NULL);
+ g_debug ("GdmUserManager: calling 'ListCachedUsers'");
+
+ dbus_g_proxy_begin_call (manager->priv->accounts_proxy,
+ "ListCachedUsers",
+ on_list_cached_users_finished,
+ manager,
+ NULL,
+ G_TYPE_INVALID);
+ manager->priv->listing_cached_users = TRUE;
+}
+
+static void
+load_seat_incrementally (GdmUserManager *manager)
+{
+ g_assert (manager->priv->seat.proxy == NULL);
+
+ switch (manager->priv->seat.state) {
+ case GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID:
+ get_current_session_id (manager);
+ break;
+ case GDM_USER_MANAGER_SEAT_STATE_GET_ID:
+ get_seat_id_for_current_session (manager);
+ break;
+ case GDM_USER_MANAGER_SEAT_STATE_GET_PROXY:
+ get_seat_proxy (manager);
+ break;
+ case GDM_USER_MANAGER_SEAT_STATE_LOADED:
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ if (manager->priv->seat.state == GDM_USER_MANAGER_SEAT_STATE_LOADED) {
+ gboolean res;
+
+ load_sessions (manager);
+ res = load_ck_history (manager);
+ }
+
+ maybe_set_is_loaded (manager);
+}
+
+static gboolean
+load_idle (GdmUserManager *manager)
+{
+ manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED + 1;
+ load_seat_incrementally (manager);
+ load_users (manager);
+ manager->priv->load_id = 0;
+
+ return FALSE;
+}
+
+static void
+queue_load_seat_and_users (GdmUserManager *manager)
+{
+ if (manager->priv->load_id > 0) {
+ return;
+ }
+
+ manager->priv->load_id = g_idle_add ((GSourceFunc)load_idle, manager);
+}
+
+static gboolean
+reload_passwd_idle (GdmUserManager *manager)
+{
+ schedule_reload_passwd (manager);
+ manager->priv->reload_passwd_id = 0;
+
+ return FALSE;
+}
+
+static void
+queue_reload_passwd (GdmUserManager *manager)
+{
+ if (manager->priv->reload_passwd_id > 0) {
+ g_source_remove (manager->priv->reload_passwd_id);
+ }
+
+ manager->priv->reload_passwd_id = g_timeout_add_seconds (RELOAD_PASSWD_THROTTLE_SECS, (GSourceFunc)reload_passwd_idle, manager);
+}
+
static void
on_shells_monitor_changed (GFileMonitor *monitor,
GFile *file,
}
reload_shells (manager);
- reload_passwd (manager);
+ queue_reload_passwd (manager);
}
static void
return;
}
- reload_passwd (manager);
+ queue_reload_passwd (manager);
}
static void
-ignore_log_handler (const char *log_domain,
- GLogLevelFlags log_level,
- const char *message,
- gpointer user_data)
+gdm_user_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
- return;
+ GdmUserManager *manager;
+
+ manager = GDM_USER_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_IS_LOADED:
+ g_value_set_boolean (value, manager->priv->is_loaded);
+ break;
+ case PROP_HAS_MULTIPLE_USERS:
+ g_value_set_boolean (value, manager->priv->has_multiple_users);
+ break;
+ case PROP_INCLUDE_ALL:
+ g_value_set_boolean (value, manager->priv->include_all);
+ break;
+ case PROP_INCLUDE_USERNAMES_LIST:
+ g_value_set_pointer (value, manager->priv->include_usernames);
+ break;
+ case PROP_EXCLUDE_USERNAMES_LIST:
+ g_value_set_pointer (value, manager->priv->exclude_usernames);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+set_include_usernames (GdmUserManager *manager,
+ GSList *list)
+{
+ if (manager->priv->include_usernames != NULL) {
+ g_slist_foreach (manager->priv->include_usernames, (GFunc) g_free, NULL);
+ g_slist_free (manager->priv->include_usernames);
+ }
+ manager->priv->include_usernames = slist_deep_copy (list);
+}
+
+static void
+set_exclude_usernames (GdmUserManager *manager,
+ GSList *list)
+{
+ if (manager->priv->exclude_usernames != NULL) {
+ g_slist_foreach (manager->priv->exclude_usernames, (GFunc) g_free, NULL);
+ g_slist_free (manager->priv->exclude_usernames);
+ }
+ manager->priv->exclude_usernames = slist_deep_copy (list);
+}
+
+static void
+set_include_all (GdmUserManager *manager,
+ gboolean all)
+{
+ if (manager->priv->include_all != all) {
+ manager->priv->include_all = all;
+ }
+}
+
+static void
+gdm_user_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmUserManager *self;
+
+ self = GDM_USER_MANAGER (object);
+
+ switch (prop_id) {
+ case PROP_INCLUDE_ALL:
+ set_include_all (self, g_value_get_boolean (value));
+ break;
+ case PROP_INCLUDE_USERNAMES_LIST:
+ set_include_usernames (self, g_value_get_pointer (value));
+ break;
+ case PROP_EXCLUDE_USERNAMES_LIST:
+ set_exclude_usernames (self, g_value_get_pointer (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
}
static void
-check_gdm_debug (void)
+monitor_local_users (GdmUserManager *manager)
{
- GDebugKey gdmkeys[] = { { "gdm", 1 } };
- const char *gnome_shell_debug = g_getenv ("GNOME_SHELL_DEBUG");
+ GFile *file;
+ GError *error;
+
+ g_debug ("GdmUserManager: Monitoring local users");
+
+ /* /etc/shells */
+ file = g_file_new_for_path (_PATH_SHELLS);
+ error = NULL;
+ manager->priv->shells_monitor = g_file_monitor_file (file,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ &error);
+ if (manager->priv->shells_monitor != NULL) {
+ g_signal_connect (manager->priv->shells_monitor,
+ "changed",
+ G_CALLBACK (on_shells_monitor_changed),
+ manager);
+ } else {
+ g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
+ g_error_free (error);
+ }
+ g_object_unref (file);
- if (!gnome_shell_debug ||
- !g_parse_debug_string (gnome_shell_debug, gdmkeys, 1)) {
- g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
- ignore_log_handler, NULL);
+ /* /etc/passwd */
+ file = g_file_new_for_path (PATH_PASSWD);
+ manager->priv->passwd_monitor = g_file_monitor_file (file,
+ G_FILE_MONITOR_NONE,
+ NULL,
+ &error);
+ if (manager->priv->passwd_monitor != NULL) {
+ g_signal_connect (manager->priv->passwd_monitor,
+ "changed",
+ G_CALLBACK (on_passwd_monitor_changed),
+ manager);
+ } else {
+ g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
+ g_error_free (error);
}
+ g_object_unref (file);
}
static void
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = gdm_user_manager_finalize;
+ object_class->get_property = gdm_user_manager_get_property;
+ object_class->set_property = gdm_user_manager_set_property;
+
+ g_object_class_install_property (object_class,
+ PROP_IS_LOADED,
+ g_param_spec_boolean ("is-loaded",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_HAS_MULTIPLE_USERS,
+ g_param_spec_boolean ("has-multiple-users",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+ g_object_class_install_property (object_class,
+ PROP_INCLUDE_ALL,
+ g_param_spec_boolean ("include-all",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READWRITE));
+ g_object_class_install_property (object_class,
+ PROP_INCLUDE_USERNAMES_LIST,
+ g_param_spec_pointer ("include-usernames-list",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
+
+ g_object_class_install_property (object_class,
+ PROP_EXCLUDE_USERNAMES_LIST,
+ g_param_spec_pointer ("exclude-usernames-list",
+ NULL,
+ NULL,
+ G_PARAM_READWRITE));
- signals [LOADING_USERS] =
- g_signal_new ("loading-users",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdmUserManagerClass, loading_users),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
- signals [USERS_LOADED] =
- g_signal_new ("users-loaded",
- G_TYPE_FROM_CLASS (klass),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdmUserManagerClass, users_loaded),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
signals [USER_ADDED] =
g_signal_new ("user-added",
G_TYPE_FROM_CLASS (klass),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GDM_TYPE_USER);
- signals [USER_LOGIN_FREQUENCY_CHANGED] =
- g_signal_new ("user-login-frequency-changed",
+ signals [USER_CHANGED] =
+ g_signal_new ("user-changed",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdmUserManagerClass, user_login_frequency_changed),
+ G_STRUCT_OFFSET (GdmUserManagerClass, user_changed),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1, GDM_TYPE_USER);
g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
- check_gdm_debug ();
+}
+
+/**
+ * gdm_user_manager_queue_load:
+ * @manager: a #GdmUserManager
+ *
+ * Queue loading users into user manager. This must be called, and the
+ * #GdmUserManager:is-loaded property must be %TRUE before calling
+ * gdm_user_manager_list_users()
+ */
+void
+gdm_user_manager_queue_load (GdmUserManager *manager)
+{
+ g_return_if_fail (GDM_IS_USER_MANAGER (manager));
+
+ if (! manager->priv->is_loaded) {
+ queue_load_seat_and_users (manager);
+ }
}
static void
gdm_user_manager_init (GdmUserManager *manager)
{
- int i;
- GFile *file;
GError *error;
- const char *exclude_default[] = DEFAULT_EXCLUDE;
manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
g_free,
g_free);
- /* exclusions */
- manager->priv->exclusions = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- NULL);
- for (i = 0; exclude_default[i] != NULL; i++) {
- g_hash_table_insert (manager->priv->exclusions,
- g_strdup (exclude_default [i]),
- GUINT_TO_POINTER (TRUE));
- }
-
- /* /etc/shells */
- manager->priv->shells = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- NULL);
- reload_shells (manager);
- file = g_file_new_for_path (_PATH_SHELLS);
- error = NULL;
- manager->priv->shells_monitor = g_file_monitor_file (file,
- G_FILE_MONITOR_NONE,
- NULL,
- &error);
- if (manager->priv->shells_monitor != NULL) {
- g_signal_connect (manager->priv->shells_monitor,
- "changed",
- G_CALLBACK (on_shells_monitor_changed),
- manager);
- } else {
- g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
- g_error_free (error);
- }
- g_object_unref (file);
+ /* users */
+ manager->priv->users_by_name = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ g_object_unref);
- /* /etc/passwd */
- manager->priv->users = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- g_free,
- (GDestroyNotify) g_object_run_dispose);
- file = g_file_new_for_path (PATH_PASSWD);
- manager->priv->passwd_monitor = g_file_monitor_file (file,
- G_FILE_MONITOR_NONE,
- NULL,
- &error);
- if (manager->priv->passwd_monitor != NULL) {
- g_signal_connect (manager->priv->passwd_monitor,
- "changed",
- G_CALLBACK (on_passwd_monitor_changed),
- manager);
- } else {
- g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
- g_error_free (error);
- }
- g_object_unref (file);
+ manager->priv->users_by_object_path = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ g_object_unref);
+ g_assert (manager->priv->seat.proxy == NULL);
- get_seat_proxy (manager);
+ error = NULL;
+ manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (manager->priv->connection == NULL) {
+ if (error != NULL) {
+ g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
+ g_error_free (error);
+ } else {
+ g_warning ("Failed to connect to the D-Bus daemon");
+ }
+ return;
+ }
- queue_reload_users (manager);
+ get_accounts_proxy (manager);
- manager->priv->users_dirty = FALSE;
+ manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED;
}
static void
gdm_user_manager_finalize (GObject *object)
{
GdmUserManager *manager;
+ GSList *node;
g_return_if_fail (object != NULL);
g_return_if_fail (GDM_IS_USER_MANAGER (object));
g_return_if_fail (manager->priv != NULL);
- if (manager->priv->seat_proxy != NULL) {
- g_object_unref (manager->priv->seat_proxy);
+ if (manager->priv->ck_history_pid > 0) {
+ g_debug ("Killing ck-history process");
+ signal_pid (manager->priv->ck_history_pid, SIGTERM);
+ }
+
+ g_slist_foreach (manager->priv->new_sessions,
+ (GFunc) unload_new_session, NULL);
+ g_slist_free (manager->priv->new_sessions);
+
+ g_slist_foreach (manager->priv->fetch_user_requests,
+ (GFunc) free_fetch_user_request, NULL);
+ g_slist_free (manager->priv->fetch_user_requests);
+
+ node = manager->priv->new_users;
+ while (node != NULL) {
+ GdmUser *user;
+ GSList *next_node;
+
+ user = GDM_USER (node->data);
+ next_node = node->next;
+
+ g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
+ g_object_unref (user);
+ manager->priv->new_users = g_slist_delete_link (manager->priv->new_users, node);
+ node = next_node;
+ }
+
+ unload_seat (manager);
+
+ if (manager->priv->exclude_usernames != NULL) {
+ g_slist_foreach (manager->priv->exclude_usernames, (GFunc) g_free, NULL);
+ g_slist_free (manager->priv->exclude_usernames);
+ }
+
+ if (manager->priv->include_usernames != NULL) {
+ g_slist_foreach (manager->priv->include_usernames, (GFunc) g_free, NULL);
+ g_slist_free (manager->priv->include_usernames);
+ }
+
+ if (manager->priv->seat.proxy != NULL) {
+ g_object_unref (manager->priv->seat.proxy);
+ }
+
+ if (manager->priv->accounts_proxy != NULL) {
+ g_object_unref (manager->priv->accounts_proxy);
}
if (manager->priv->ck_history_id != 0) {
manager->priv->ck_history_id = 0;
}
- if (manager->priv->reload_id > 0) {
- g_source_remove (manager->priv->reload_id);
- manager->priv->reload_id = 0;
+ if (manager->priv->ck_history_watchdog_id != 0) {
+ g_source_remove (manager->priv->ck_history_watchdog_id);
+ manager->priv->ck_history_watchdog_id = 0;
+ }
+
+ if (manager->priv->load_id > 0) {
+ g_source_remove (manager->priv->load_id);
+ manager->priv->load_id = 0;
+ }
+
+ if (manager->priv->reload_passwd_id > 0) {
+ g_source_remove (manager->priv->reload_passwd_id);
+ manager->priv->reload_passwd_id = 0;
}
g_hash_table_destroy (manager->priv->sessions);
- g_file_monitor_cancel (manager->priv->passwd_monitor);
- g_hash_table_destroy (manager->priv->users);
+ if (manager->priv->passwd_monitor != NULL) {
+ g_file_monitor_cancel (manager->priv->passwd_monitor);
+ }
+
+ g_hash_table_destroy (manager->priv->users_by_name);
+ g_hash_table_destroy (manager->priv->users_by_object_path);
- g_file_monitor_cancel (manager->priv->shells_monitor);
- g_hash_table_destroy (manager->priv->shells);
+ if (manager->priv->shells_monitor != NULL) {
+ g_file_monitor_cancel (manager->priv->shells_monitor);
+ }
- g_free (manager->priv->seat_id);
+ if (manager->priv->shells != NULL) {
+ g_hash_table_destroy (manager->priv->shells);
+ }
G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
}
/**
* gdm_user_manager_ref_default:
*
- * Returns: (transfer full): The singleton #GdmUserManager object.
+ * Queue loading users into user manager. This must be called, and the
+ * #GdmUserManager:is-loaded property must be %TRUE before calling
+ * gdm_user_manager_list_users()
+ *
+ * Returns: (transfer full): user manager object
*/
GdmUserManager *
gdm_user_manager_ref_default (void)
#include <sys/stat.h>
#include <unistd.h>
-/* Note on sync with gdm; need to use -lib here */
-#include <glib/gi18n-lib.h>
+#include <dbus/dbus-glib.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
-#include "gdm-user-manager.h"
#include "gdm-user-private.h"
#define GDM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_USER, GdmUserClass))
#define GDM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), GDM_TYPE_USER, GdmUserClass))
#define GLOBAL_FACEDIR DATADIR "/faces"
-#define MAX_ICON_SIZE 128
#define MAX_FILE_SIZE 65536
-#define MINIMAL_UID 100
-#define RELAX_GROUP TRUE
-#define RELAX_OTHER TRUE
+
+#define ACCOUNTS_NAME "org.freedesktop.Accounts"
+#define ACCOUNTS_USER_INTERFACE "org.freedesktop.Accounts.User"
enum {
PROP_0,
- PROP_MANAGER,
- PROP_REAL_NAME,
- PROP_USER_NAME,
- PROP_UID,
- PROP_HOME_DIR,
- PROP_SHELL,
- PROP_LOGIN_FREQUENCY,
+ PROP_IS_LOADED
};
enum {
- ICON_CHANGED,
+ CHANGED,
SESSIONS_CHANGED,
LAST_SIGNAL
};
struct _GdmUser {
GObject parent;
- GdmUserManager *manager;
+ DBusGConnection *connection;
+ DBusGProxy *accounts_proxy;
+ DBusGProxy *object_proxy;
+ DBusGProxyCall *get_all_call;
+ char *object_path;
uid_t uid;
char *user_name;
char *real_name;
- char *home_dir;
- char *shell;
+ char *icon_file;
GList *sessions;
gulong login_frequency;
- GFileMonitor *icon_monitor;
+ guint is_loaded : 1;
};
struct _GdmUserClass
{
GObjectClass parent_class;
-
- void (* icon_changed) (GdmUser *user);
- void (* sessions_changed) (GdmUser *user);
};
static void gdm_user_finalize (GObject *object);
+static gboolean check_user_file (const char *filename);
static guint signals[LAST_SIGNAL] = { 0 };
return g_list_length (user->sessions);
}
-/**
- * gdm_user_get_sessions:
- * @user: a #GdmUser
- *
- * Returns: (transfer none) (element-type utf8): Session identifier strings
- */
-GList *
-gdm_user_get_sessions (GdmUser *user)
-{
- return user->sessions;
-}
-
static void
-_gdm_user_set_login_frequency (GdmUser *user,
- gulong login_frequency)
-{
- user->login_frequency = login_frequency;
- g_object_notify (G_OBJECT (user), "login-frequency");
-}
-
-static void
-gdm_user_set_property (GObject *object,
- guint param_id,
- const GValue *value,
- GParamSpec *pspec)
+gdm_user_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
{
GdmUser *user;
user = GDM_USER (object);
- switch (param_id) {
- case PROP_MANAGER:
- user->manager = g_value_get_object (value);
- g_assert (user->manager);
- break;
- case PROP_LOGIN_FREQUENCY:
- _gdm_user_set_login_frequency (user, g_value_get_ulong (value));
- break;
+ switch (prop_id) {
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
-gdm_user_get_property (GObject *object,
- guint param_id,
- GValue *value,
- GParamSpec *pspec)
+gdm_user_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
{
GdmUser *user;
user = GDM_USER (object);
- switch (param_id) {
- case PROP_MANAGER:
- g_value_set_object (value, user->manager);
- break;
- case PROP_USER_NAME:
- g_value_set_string (value, user->user_name);
- break;
- case PROP_REAL_NAME:
- g_value_set_string (value, user->real_name);
- break;
- case PROP_HOME_DIR:
- g_value_set_string (value, user->home_dir);
- break;
- case PROP_UID:
- g_value_set_ulong (value, user->uid);
- break;
- case PROP_SHELL:
- g_value_set_string (value, user->shell);
- break;
- case PROP_LOGIN_FREQUENCY:
- g_value_set_ulong (value, user->login_frequency);
+ switch (prop_id) {
+ case PROP_IS_LOADED:
+ g_value_set_boolean (value, user->is_loaded);
break;
default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
gobject_class = G_OBJECT_CLASS (class);
+ gobject_class->finalize = gdm_user_finalize;
gobject_class->set_property = gdm_user_set_property;
gobject_class->get_property = gdm_user_get_property;
- gobject_class->finalize = gdm_user_finalize;
-
- g_object_class_install_property (gobject_class,
- PROP_MANAGER,
- g_param_spec_object ("manager",
- "Manager",
- "The user manager object this user is controlled by.",
- GDM_TYPE_USER_MANAGER,
- (G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT_ONLY)));
-
- g_object_class_install_property (gobject_class,
- PROP_REAL_NAME,
- g_param_spec_string ("real-name",
- "Real Name",
- "The real name to display for this user.",
- NULL,
- G_PARAM_READABLE));
g_object_class_install_property (gobject_class,
- PROP_UID,
- g_param_spec_ulong ("uid",
- "User ID",
- "The UID for this user.",
- 0, G_MAXULONG, 0,
- G_PARAM_READABLE));
- g_object_class_install_property (gobject_class,
- PROP_USER_NAME,
- g_param_spec_string ("user-name",
- "User Name",
- "The login name for this user.",
- NULL,
- G_PARAM_READABLE));
- g_object_class_install_property (gobject_class,
- PROP_HOME_DIR,
- g_param_spec_string ("home-directory",
- "Home Directory",
- "The home directory for this user.",
- NULL,
- G_PARAM_READABLE));
- g_object_class_install_property (gobject_class,
- PROP_SHELL,
- g_param_spec_string ("shell",
- "Shell",
- "The shell for this user.",
- NULL,
- G_PARAM_READABLE));
- g_object_class_install_property (gobject_class,
- PROP_LOGIN_FREQUENCY,
- g_param_spec_ulong ("login-frequency",
- "login frequency",
- "login frequency",
- 0,
- G_MAXULONG,
- 0,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
-
- signals [ICON_CHANGED] =
- g_signal_new ("icon-changed",
+ PROP_IS_LOADED,
+ g_param_spec_boolean ("is-loaded",
+ NULL,
+ NULL,
+ FALSE,
+ G_PARAM_READABLE));
+
+ signals [CHANGED] =
+ g_signal_new ("changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdmUserClass, icon_changed),
+ 0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
g_signal_new ("sessions-changed",
G_TYPE_FROM_CLASS (class),
G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (GdmUserClass, sessions_changed),
+ 0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
-
-static void
-on_icon_monitor_changed (GFileMonitor *monitor,
- GFile *file,
- GFile *other_file,
- GFileMonitorEvent event_type,
- GdmUser *user)
-{
- g_debug ("Icon changed: %d", event_type);
-
- if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
- event_type != G_FILE_MONITOR_EVENT_CREATED) {
- return;
- }
-
- _gdm_user_icon_changed (user);
-}
-
static void
-update_icon_monitor (GdmUser *user)
+gdm_user_init (GdmUser *user)
{
- GFile *file;
GError *error;
- char *path;
-
- if (user->home_dir == NULL) {
- return;
- }
- if (user->icon_monitor != NULL) {
- g_file_monitor_cancel (user->icon_monitor);
- user->icon_monitor = NULL;
- }
-
- path = g_build_filename (user->home_dir, ".face", NULL);
- g_debug ("adding monitor for '%s'", path);
- file = g_file_new_for_path (path);
- error = NULL;
- user->icon_monitor = g_file_monitor_file (file,
- G_FILE_MONITOR_NONE,
- NULL,
- &error);
- if (user->icon_monitor != NULL) {
- g_signal_connect (user->icon_monitor,
- "changed",
- G_CALLBACK (on_icon_monitor_changed),
- user);
- } else {
- g_warning ("Unable to monitor %s: %s", path, error->message);
- g_error_free (error);
- }
- g_object_unref (file);
- g_free (path);
-}
-
-static void
-gdm_user_init (GdmUser *user)
-{
- user->manager = NULL;
user->user_name = NULL;
user->real_name = NULL;
user->sessions = NULL;
+
+ error = NULL;
+ user->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (user->connection == NULL) {
+ g_warning ("Couldn't connect to system bus: %s", error->message);
+ }
}
static void
user = GDM_USER (object);
- g_file_monitor_cancel (user->icon_monitor);
-
g_free (user->user_name);
g_free (user->real_name);
+ g_free (user->icon_file);
+ g_free (user->object_path);
+
+ if (user->accounts_proxy != NULL) {
+ g_object_unref (user->accounts_proxy);
+ }
+
+ if (user->object_proxy != NULL) {
+ g_object_unref (user->object_proxy);
+ }
+
+ if (user->connection != NULL) {
+ dbus_g_connection_unref (user->connection);
+ }
if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
(*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
}
+static void
+set_is_loaded (GdmUser *user,
+ gboolean is_loaded)
+{
+ if (user->is_loaded != is_loaded) {
+ user->is_loaded = is_loaded;
+ g_object_notify (G_OBJECT (user), "is-loaded");
+ }
+}
+
/**
- * _gdm_user_update:
+ * _gdm_user_update_from_pwent:
* @user: the user object to update.
* @pwent: the user data to use.
*
* Updates the properties of @user using the data in @pwent.
- *
- * Since: 1.0
**/
void
-_gdm_user_update (GdmUser *user,
- const struct passwd *pwent)
+_gdm_user_update_from_pwent (GdmUser *user,
+ const struct passwd *pwent)
{
- gchar *real_name;
+ gchar *real_name = NULL;
+ gboolean changed;
g_return_if_fail (GDM_IS_USER (user));
g_return_if_fail (pwent != NULL);
+ g_return_if_fail (user->object_path == NULL);
- g_object_freeze_notify (G_OBJECT (user));
+ changed = FALSE;
/* Display Name */
if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
- gchar *first_comma;
- gchar *real_name_utf8;
+ gchar *first_comma = NULL;
+ gchar *valid_utf8_name = NULL;
- real_name_utf8 = g_locale_to_utf8 (pwent->pw_gecos, -1, NULL, NULL, NULL);
+ if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
+ valid_utf8_name = pwent->pw_gecos;
+ first_comma = strchr (valid_utf8_name, ',');
+ } else {
+ g_warning ("User %s has invalid UTF-8 in GECOS field. "
+ "It would be a good thing to check /etc/passwd.",
+ pwent->pw_name ? pwent->pw_name : "");
+ }
- first_comma = strchr (real_name_utf8, ',');
if (first_comma) {
- real_name = g_strndup (real_name_utf8, first_comma - real_name_utf8);
- g_free (real_name_utf8);
+ real_name = g_strndup (valid_utf8_name,
+ (first_comma - valid_utf8_name));
+ } else if (valid_utf8_name) {
+ real_name = g_strdup (valid_utf8_name);
} else {
- real_name = real_name_utf8;
+ real_name = NULL;
}
- if (real_name[0] == '\0') {
+ if (real_name && real_name[0] == '\0') {
g_free (real_name);
real_name = NULL;
}
real_name = NULL;
}
- if ((real_name && !user->real_name) ||
- (!real_name && user->real_name) ||
- (real_name &&
- user->real_name &&
- strcmp (real_name, user->real_name) != 0)) {
+ if (g_strcmp0 (real_name, user->real_name) != 0) {
g_free (user->real_name);
user->real_name = real_name;
- g_object_notify (G_OBJECT (user), "real-name");
+ changed = TRUE;
} else {
g_free (real_name);
}
/* UID */
if (pwent->pw_uid != user->uid) {
user->uid = pwent->pw_uid;
- g_object_notify (G_OBJECT (user), "uid");
+ changed = TRUE;
}
/* Username */
- if ((pwent->pw_name && !user->user_name) ||
- (!pwent->pw_name && user->user_name) ||
- (pwent->pw_name &&
- user->user_name &&
- strcmp (user->user_name, pwent->pw_name) != 0)) {
+ if (g_strcmp0 (pwent->pw_name, user->user_name) != 0) {
+ g_free (user->icon_file);
+ user->icon_file = NULL;
+ if (pwent->pw_name != NULL) {
+ gboolean res;
+
+ user->icon_file = g_build_filename (GDM_CACHE_DIR, pwent->pw_name, "face", NULL);
+
+ res = check_user_file (user->icon_file);
+ if (!res) {
+ g_free (user->icon_file);
+ user->icon_file = g_build_filename (GLOBAL_FACEDIR, pwent->pw_name, NULL);
+ }
+ }
+
g_free (user->user_name);
user->user_name = g_strdup (pwent->pw_name);
- g_object_notify (G_OBJECT (user), "user-name");
+ changed = TRUE;
}
- /* Home Directory */
- if ((pwent->pw_dir && !user->home_dir) ||
- (!pwent->pw_dir && user->home_dir) ||
- strcmp (user->home_dir, pwent->pw_dir) != 0) {
- g_free (user->home_dir);
- user->home_dir = g_strdup (pwent->pw_dir);
- g_object_notify (G_OBJECT (user), "home-directory");
- g_signal_emit (user, signals[ICON_CHANGED], 0);
+ if (!user->is_loaded) {
+ set_is_loaded (user, TRUE);
}
- /* Shell */
- if ((pwent->pw_shell && !user->shell) ||
- (!pwent->pw_shell && user->shell) ||
- (pwent->pw_shell &&
- user->shell &&
- strcmp (user->shell, pwent->pw_shell) != 0)) {
- g_free (user->shell);
- user->shell = g_strdup (pwent->pw_shell);
- g_object_notify (G_OBJECT (user), "shell");
+ if (changed) {
+ g_signal_emit (user, signals[CHANGED], 0);
}
-
- update_icon_monitor (user);
-
- g_object_thaw_notify (G_OBJECT (user));
}
/**
- * _gdm_user_icon_changed:
- * @user: the user to emit the signal for.
- *
- * Emits the "icon-changed" signal for @user.
+ * _gdm_user_update_login_frequency:
+ * @user: the user object to update
+ * @login_frequency: the number of times the user has logged in
*
- * Since: 1.0
+ * Updates the login frequency of @user
**/
void
-_gdm_user_icon_changed (GdmUser *user)
+_gdm_user_update_login_frequency (GdmUser *user,
+ guint64 login_frequency)
{
g_return_if_fail (GDM_IS_USER (user));
- g_signal_emit (user, signals[ICON_CHANGED], 0);
+ if (login_frequency == user->login_frequency) {
+ return;
+ }
+
+ user->login_frequency = login_frequency;
+ g_signal_emit (user, signals[CHANGED], 0);
}
/**
*
* Retrieves the ID of @user.
*
- * Returns: a pointer to an array of characters which must not be modified or
+ * Returns: (transfer none): a pointer to an array of characters which must not be modified or
* freed, or %NULL.
- *
- * Since: 1.0
**/
gulong
*
* Retrieves the display name of @user.
*
- * Returns: a pointer to an array of characters which must not be modified or
+ * Returns: (transfer none): a pointer to an array of characters which must not be modified or
* freed, or %NULL.
- *
- * Since: 1.0
**/
-G_CONST_RETURN gchar *
+const char *
gdm_user_get_real_name (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), NULL);
*
* Retrieves the login name of @user.
*
- * Returns: a pointer to an array of characters which must not be modified or
+ * Returns: (transfer none): a pointer to an array of characters which must not be modified or
* freed, or %NULL.
- *
- * Since: 1.0
**/
-G_CONST_RETURN gchar *
+const char *
gdm_user_get_user_name (GdmUser *user)
{
g_return_val_if_fail (GDM_IS_USER (user), NULL);
}
/**
- * gdm_user_get_home_directory:
- * @user: the user object to examine.
- *
- * Retrieves the home directory of @user.
- *
- * Returns: a pointer to an array of characters which must not be modified or
- * freed, or %NULL.
- *
- * Since: 1.0
- **/
-
-G_CONST_RETURN gchar *
-gdm_user_get_home_directory (GdmUser *user)
-{
- g_return_val_if_fail (GDM_IS_USER (user), NULL);
-
- return user->home_dir;
-}
-
-/**
- * gdm_user_get_shell:
- * @user: the user object to examine.
- *
- * Retrieves the login shell of @user.
+ * gdm_user_get_login_frequency:
+ * @user: a #GdmUser
*
- * Returns: a pointer to an array of characters which must not be modified or
- * freed, or %NULL.
+ * Returns the number of times @user has logged in.
*
- * Since: 1.0
- **/
-
-G_CONST_RETURN gchar *
-gdm_user_get_shell (GdmUser *user)
-{
- g_return_val_if_fail (GDM_IS_USER (user), NULL);
-
- return user->shell;
-}
-
+ * Returns: the login frequency
+ */
gulong
gdm_user_get_login_frequency (GdmUser *user)
{
const char *str2;
gulong num1;
gulong num2;
+ guint len1;
+ guint len2;
+
+ g_return_val_if_fail (GDM_IS_USER (user1), 0);
+ g_return_val_if_fail (GDM_IS_USER (user2), 0);
+
+ num1 = user1->login_frequency;
+ num2 = user2->login_frequency;
+
+ if (num1 > num2) {
+ return -1;
+ }
+
+ if (num1 < num2) {
+ return 1;
+ }
- g_return_val_if_fail (user1 == NULL || GDM_IS_USER (user1), 0);
- g_return_val_if_fail (user2 == NULL || GDM_IS_USER (user2), 0);
+ len1 = g_list_length (user1->sessions);
+ len2 = g_list_length (user2->sessions);
+
+ if (len1 > len2) {
+ return -1;
+ }
+
+ if (len1 < len2) {
+ return 1;
+ }
+
+ /* if login frequency is equal try names */
if (user1->real_name != NULL) {
str1 = user1->real_name;
} else {
str2 = user2->user_name;
}
- num1 = user1->login_frequency;
- num2 = user2->login_frequency;
- g_debug ("Login freq 1=%u 2=%u", (guint)num1, (guint)num2);
- if (num1 > num2) {
- return -1;
- }
-
- if (num1 < num2) {
- return 1;
- }
-
- /* if login frequency is equal try names */
if (str1 == NULL && str2 != NULL) {
return -1;
}
}
static gboolean
-check_user_file (const char *filename,
- uid_t user,
- gssize max_file_size,
- gboolean relax_group,
- gboolean relax_other)
+check_user_file (const char *filename)
{
+ gssize max_file_size = MAX_FILE_SIZE;
struct stat fileinfo;
- if (max_file_size < 0) {
- max_file_size = G_MAXSIZE;
- }
-
/* Exists/Readable? */
if (stat (filename, &fileinfo) < 0) {
return FALSE;
return FALSE;
}
- /* Owned by user? */
- if (G_UNLIKELY (fileinfo.st_uid != user)) {
- return FALSE;
- }
-
- /* Group not writable or relax_group? */
- if (G_UNLIKELY ((fileinfo.st_mode & S_IWGRP) == S_IWGRP && !relax_group)) {
- return FALSE;
- }
-
- /* Other not writable or relax_other? */
- if (G_UNLIKELY ((fileinfo.st_mode & S_IWOTH) == S_IWOTH && !relax_other)) {
- return FALSE;
- }
-
/* Size is kosher? */
if (G_UNLIKELY (fileinfo.st_size > max_file_size)) {
return FALSE;
return TRUE;
}
-static char *
-get_filesystem_type (const char *path)
-{
- GFile *file;
- GFileInfo *file_info;
- GError *error;
- char *filesystem_type;
-
- file = g_file_new_for_path (path);
- error = NULL;
- file_info = g_file_query_filesystem_info (file,
- G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
- NULL,
- &error);
- if (file_info == NULL) {
- g_warning ("Unable to query filesystem type for %s: %s", path, error->message);
- g_error_free (error);
- g_object_unref (file);
- return NULL;
- }
-
- filesystem_type = g_strdup (g_file_info_get_attribute_string (file_info,
- G_FILE_ATTRIBUTE_FILESYSTEM_TYPE));
- if (filesystem_type == NULL) {
- g_warning ("GIO returned NULL filesystem type for %s", path);
- }
-
- g_object_unref (file);
- g_object_unref (file_info);
-
- return filesystem_type;
-}
-
-static GdkPixbuf *
-render_icon_from_home (GdmUser *user,
- int icon_size)
-{
- GdkPixbuf *retval;
- char *path;
- gboolean is_local;
- gboolean is_autofs;
- gboolean res;
- char *filesystem_type;
-
- is_local = FALSE;
-
- /* special case: look at parent of home to detect autofs
- this is so we don't try to trigger an automount */
- path = g_path_get_dirname (user->home_dir);
- filesystem_type = get_filesystem_type (path);
- is_autofs = (filesystem_type != NULL && strcmp (filesystem_type, "autofs") == 0);
- g_free (filesystem_type);
- g_free (path);
-
- if (is_autofs) {
- return NULL;
- }
-
- /* now check that home dir itself is local */
- filesystem_type = get_filesystem_type (user->home_dir);
- is_local = ((filesystem_type != NULL) &&
- (strcmp (filesystem_type, "nfs") != 0) &&
- (strcmp (filesystem_type, "afs") != 0) &&
- (strcmp (filesystem_type, "autofs") != 0) &&
- (strcmp (filesystem_type, "unknown") != 0) &&
- (strcmp (filesystem_type, "ncpfs") != 0));
- g_free (filesystem_type);
-
- /* only look at local home directories so we don't try to
- read from remote (e.g. NFS) volumes */
- if (! is_local) {
- return NULL;
- }
-
- /* First, try "~/.face" */
- path = g_build_filename (user->home_dir, ".face", NULL);
- res = check_user_file (path,
- user->uid,
- MAX_FILE_SIZE,
- RELAX_GROUP,
- RELAX_OTHER);
- if (res) {
- retval = gdk_pixbuf_new_from_file_at_size (path,
- icon_size,
- icon_size,
- NULL);
- } else {
- retval = NULL;
- }
- g_free (path);
-
- /* Next, try "~/.face.icon" */
- if (retval == NULL) {
- path = g_build_filename (user->home_dir,
- ".face.icon",
- NULL);
- res = check_user_file (path,
- user->uid,
- MAX_FILE_SIZE,
- RELAX_GROUP,
- RELAX_OTHER);
- if (res) {
- retval = gdk_pixbuf_new_from_file_at_size (path,
- icon_size,
- icon_size,
- NULL);
- } else {
- retval = NULL;
- }
-
- g_free (path);
- }
-
- /* Still nothing, try the user's personal GDM config */
- if (retval == NULL) {
- path = g_build_filename (user->home_dir,
- ".gnome",
- "gdm",
- NULL);
- res = check_user_file (path,
- user->uid,
- MAX_FILE_SIZE,
- RELAX_GROUP,
- RELAX_OTHER);
- if (res) {
- GKeyFile *keyfile;
- char *icon_path;
-
- keyfile = g_key_file_new ();
- g_key_file_load_from_file (keyfile,
- path,
- G_KEY_FILE_NONE,
- NULL);
-
- icon_path = g_key_file_get_string (keyfile,
- "face",
- "picture",
- NULL);
- res = check_user_file (icon_path,
- user->uid,
- MAX_FILE_SIZE,
- RELAX_GROUP,
- RELAX_OTHER);
- if (icon_path && res) {
- retval = gdk_pixbuf_new_from_file_at_size (path,
- icon_size,
- icon_size,
- NULL);
- } else {
- retval = NULL;
- }
-
- g_free (icon_path);
- g_key_file_free (keyfile);
- } else {
- retval = NULL;
- }
-
- g_free (path);
- }
-
- return retval;
-}
-
static void
-curved_rectangle (cairo_t *cr,
- double x0,
- double y0,
- double width,
- double height,
- double radius)
+rounded_rectangle (cairo_t *cr,
+ gdouble aspect,
+ gdouble x,
+ gdouble y,
+ gdouble corner_radius,
+ gdouble width,
+ gdouble height)
{
- double x1;
- double y1;
-
- x1 = x0 + width;
- y1 = y0 + height;
-
- if (width < FLT_EPSILON || height < FLT_EPSILON) {
- return;
- }
-
- if (width / 2 < radius) {
- if (height / 2 < radius) {
- cairo_move_to (cr, x0, (y0 + y1) / 2);
- cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0);
- cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
- cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
- cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
- } else {
- cairo_move_to (cr, x0, y0 + radius);
- cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0);
- cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
- cairo_line_to (cr, x1, y1 - radius);
- cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
- cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
- }
- } else {
- if (height / 2 < radius) {
- cairo_move_to (cr, x0, (y0 + y1) / 2);
- cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0);
- cairo_line_to (cr, x1 - radius, y0);
- cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
- cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
- cairo_line_to (cr, x0 + radius, y1);
- cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
- } else {
- cairo_move_to (cr, x0, y0 + radius);
- cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
- cairo_line_to (cr, x1 - radius, y0);
- cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
- cairo_line_to (cr, x1, y1 - radius);
- cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
- cairo_line_to (cr, x0 + radius, y1);
- cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
- }
- }
-
+ gdouble radius;
+ gdouble degrees;
+
+ radius = corner_radius / aspect;
+ degrees = G_PI / 180.0;
+
+ cairo_new_sub_path (cr);
+ cairo_arc (cr,
+ x + width - radius,
+ y + radius,
+ radius,
+ -90 * degrees,
+ 0 * degrees);
+ cairo_arc (cr,
+ x + width - radius,
+ y + height - radius,
+ radius,
+ 0 * degrees,
+ 90 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + height - radius,
+ radius,
+ 90 * degrees,
+ 180 * degrees);
+ cairo_arc (cr,
+ x + radius,
+ y + radius,
+ radius,
+ 180 * degrees,
+ 270 * degrees);
cairo_close_path (cr);
}
w = gdk_pixbuf_get_width (source) + frame_width * 2;
h = gdk_pixbuf_get_height (source) + frame_width * 2;
- radius = w / 3.0;
+ radius = w / 10;
dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
TRUE,
cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
cairo_fill (cr);
- curved_rectangle (cr, frame_width, frame_width,
- w - frame_width * 2, h - frame_width * 2,
- radius);
+ rounded_rectangle (cr,
+ 1.0,
+ frame_width + 0.5,
+ frame_width + 0.5,
+ radius,
+ w - frame_width * 2 - 1,
+ h - frame_width * 2 - 1);
cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
cairo_fill_preserve (cr);
return dest;
}
+/**
+ * gdm_user_is_logged_in:
+ * @user: a #GdmUser
+ *
+ * Returns whether or not #GdmUser is currently logged in.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean
+gdm_user_is_logged_in (GdmUser *user)
+{
+ return user->sessions != NULL;
+}
+
/**
* gdm_user_render_icon:
- * @user: a #GdmUser:
- * @icon_size: icon size in pixels
+ * @user: a #GdmUser
+ * @icon_size: the size to render the icon at
+ *
+ * Returns a #GdkPixbuf of the account icon belonging to @user
+ * at the pixel size specified by @icon_size.
*
- * Returns: (transfer full): A new icon for the user
+ * Returns: (transfer full): a #GdkPixbuf
*/
GdkPixbuf *
gdm_user_render_icon (GdmUser *user,
{
GdkPixbuf *pixbuf;
GdkPixbuf *framed;
- char *path;
- char *tmp;
gboolean res;
+ GError *error;
g_return_val_if_fail (GDM_IS_USER (user), NULL);
g_return_val_if_fail (icon_size > 12, NULL);
- path = NULL;
-
- pixbuf = render_icon_from_home (user, icon_size);
- if (pixbuf != NULL) {
- goto out;
- }
-
- /* Try ${GlobalFaceDir}/${username} */
- path = g_build_filename (GLOBAL_FACEDIR, user->user_name, NULL);
- res = check_user_file (path,
- user->uid,
- MAX_FILE_SIZE,
- RELAX_GROUP,
- RELAX_OTHER);
- if (res) {
- pixbuf = gdk_pixbuf_new_from_file_at_size (path,
- icon_size,
- icon_size,
- NULL);
- } else {
- pixbuf = NULL;
+ pixbuf = NULL;
+ if (user->icon_file) {
+ res = check_user_file (user->icon_file);
+ if (res) {
+ pixbuf = gdk_pixbuf_new_from_file_at_size (user->icon_file,
+ icon_size,
+ icon_size,
+ NULL);
+ } else {
+ pixbuf = NULL;
+ }
}
- g_free (path);
if (pixbuf != NULL) {
goto out;
}
- /* Finally, ${GlobalFaceDir}/${username}.png */
- tmp = g_strconcat (user->user_name, ".png", NULL);
- path = g_build_filename (GLOBAL_FACEDIR, tmp, NULL);
- g_free (tmp);
- res = check_user_file (path,
- user->uid,
- MAX_FILE_SIZE,
- RELAX_GROUP,
- RELAX_OTHER);
- if (res) {
- pixbuf = gdk_pixbuf_new_from_file_at_size (path,
- icon_size,
- icon_size,
- NULL);
- } else {
- pixbuf = NULL;
+ error = NULL;
+ pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+
+ "avatar-default",
+ icon_size,
+ GTK_ICON_LOOKUP_FORCE_SIZE,
+ &error);
+ if (error) {
+ g_warning ("%s", error->message);
+ g_error_free (error);
}
- g_free (path);
out:
if (pixbuf != NULL) {
return pixbuf;
}
+
+/**
+ * gdm_user_get_icon_file:
+ * @user: a #GdmUser
+ *
+ * Returns the path to the account icon belonging to @user.
+ *
+ * Returns: (transfer none): a path to an icon
+ */
+const char *
+gdm_user_get_icon_file (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->icon_file;
+}
+
+/**
+ * gdm_user_get_object_path:
+ * @user: a #GdmUser
+ *
+ * Returns the user accounts service object path of @user,
+ * or %NULL if @user doesn't have an object path associated
+ * with it.
+ *
+ * Returns: (transfer none): the primary ConsoleKit session id of the user
+ */
+const char *
+gdm_user_get_object_path (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->object_path;
+}
+
+/**
+ * gdm_user_get_primary_session_id:
+ * @user: a #GdmUser
+ *
+ * Returns the primary ConsoleKit session id of @user, or %NULL if @user isn't
+ * logged in.
+ *
+ * Returns: (transfer none): the primary ConsoleKit session id of the user
+ */
+const char *
+gdm_user_get_primary_session_id (GdmUser *user)
+{
+ if (!gdm_user_is_logged_in (user)) {
+ g_debug ("User %s is not logged in, so has no primary session",
+ gdm_user_get_user_name (user));
+ return NULL;
+ }
+
+ /* FIXME: better way to choose? */
+ return user->sessions->data;
+}
+
+static void
+collect_props (const gchar *key,
+ const GValue *value,
+ GdmUser *user)
+{
+ gboolean handled = TRUE;
+
+ if (strcmp (key, "Uid") == 0) {
+ user->uid = g_value_get_uint64 (value);
+ } else if (strcmp (key, "UserName") == 0) {
+ g_free (user->user_name);
+ user->user_name = g_value_dup_string (value);
+ } else if (strcmp (key, "RealName") == 0) {
+ g_free (user->real_name);
+ user->real_name = g_value_dup_string (value);
+ } else if (strcmp (key, "AccountType") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "Email") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "Language") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "Location") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "LoginFrequency") == 0) {
+ user->login_frequency = g_value_get_uint64 (value);
+ } else if (strcmp (key, "IconFile") == 0) {
+ gboolean res;
+
+ g_free (user->icon_file);
+ user->icon_file = g_value_dup_string (value);
+
+ res = check_user_file (user->icon_file);
+ if (!res) {
+ g_free (user->icon_file);
+ user->icon_file = g_build_filename (GLOBAL_FACEDIR, user->user_name, NULL);
+ }
+ } else if (strcmp (key, "Locked") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "AutomaticLogin") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "PasswordMode") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "PasswordHint") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "HomeDirectory") == 0) {
+ /* ignore */
+ } else if (strcmp (key, "Shell") == 0) {
+ /* ignore */
+ } else {
+ handled = FALSE;
+ }
+
+ if (!handled) {
+ g_debug ("unhandled property %s", key);
+ }
+}
+
+static void
+on_get_all_finished (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ GdmUser *user)
+{
+ GError *error;
+ GHashTable *hash_table;
+ gboolean res;
+
+ g_assert (user->get_all_call == call);
+ g_assert (user->object_proxy == proxy);
+
+ error = NULL;
+ res = dbus_g_proxy_end_call (proxy,
+ call,
+ &error,
+ dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
+ &hash_table,
+ G_TYPE_INVALID);
+ user->get_all_call = NULL;
+ user->object_proxy = NULL;
+
+ if (! res) {
+ g_debug ("Error calling GetAll() when retrieving properties for %s: %s",
+ user->object_path, error->message);
+ g_error_free (error);
+ goto out;
+ }
+ g_hash_table_foreach (hash_table, (GHFunc) collect_props, user);
+ g_hash_table_unref (hash_table);
+
+ if (!user->is_loaded) {
+ set_is_loaded (user, TRUE);
+ }
+
+ g_signal_emit (user, signals[CHANGED], 0);
+
+out:
+ g_object_unref (proxy);
+}
+
+static gboolean
+update_info (GdmUser *user)
+{
+ DBusGProxy *proxy;
+ DBusGProxyCall *call;
+
+ proxy = dbus_g_proxy_new_for_name (user->connection,
+ ACCOUNTS_NAME,
+ user->object_path,
+ DBUS_INTERFACE_PROPERTIES);
+
+ call = dbus_g_proxy_begin_call (proxy,
+ "GetAll",
+ (DBusGProxyCallNotify)
+ on_get_all_finished,
+ user,
+ NULL,
+ G_TYPE_STRING,
+ ACCOUNTS_USER_INTERFACE,
+ G_TYPE_INVALID);
+
+ if (call == NULL) {
+ g_warning ("GdmUser: failed to make GetAll call");
+ goto failed;
+ }
+
+ user->get_all_call = call;
+ user->object_proxy = proxy;
+ return TRUE;
+
+failed:
+ if (proxy != NULL) {
+ g_object_unref (proxy);
+ }
+
+ return FALSE;
+}
+
+static void
+changed_handler (DBusGProxy *proxy,
+ gpointer *data)
+{
+ GdmUser *user = GDM_USER (data);
+
+ update_info (user);
+}
+
+/**
+ * _gdm_user_update_from_object_path:
+ * @user: the user object to update.
+ * @object_path: the object path of the user to use.
+ *
+ * Updates the properties of @user from the accounts service via
+ * the object path in @object_path.
+ **/
+void
+_gdm_user_update_from_object_path (GdmUser *user,
+ const char *object_path)
+{
+ g_return_if_fail (GDM_IS_USER (user));
+ g_return_if_fail (object_path != NULL);
+ g_return_if_fail (user->object_path == NULL);
+
+ user->object_path = g_strdup (object_path);
+
+ user->accounts_proxy = dbus_g_proxy_new_for_name (user->connection,
+ ACCOUNTS_NAME,
+ user->object_path,
+ ACCOUNTS_USER_INTERFACE);
+ dbus_g_proxy_set_default_timeout (user->accounts_proxy, INT_MAX);
+ dbus_g_proxy_add_signal (user->accounts_proxy, "Changed", G_TYPE_INVALID);
+
+ dbus_g_proxy_connect_signal (user->accounts_proxy, "Changed",
+ G_CALLBACK (changed_handler), user, NULL);
+
+ if (!update_info (user)) {
+ g_warning ("Couldn't update info for user with object path %s", object_path);
+ }
+}
+
+/**
+ * gdm_user_is_loaded:
+ * @user: a #GdmUser
+ *
+ * Determines whether or not the user object is loaded and ready to read from.
+ * #GdmUserManager:is-loaded property must be %TRUE before calling
+ * gdm_user_manager_list_users()
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean
+gdm_user_is_loaded (GdmUser *user)
+{
+ return user->is_loaded;
+}